Commit 67796b3ff0c3503a78d45646568c7fae4f545d22

Authored by m-holger
1 parent 5e7b37e1

Refactor `NNTree` implementation to simplify key validation logic

- Removed `NNTreeDetails` and moved key validation logic directly into `NNTreeImpl`.
- Replaced type determination with `qpdf_object_type_e` for improved clarity.
- Updated `QPDFNameTreeObjectHelper` and `QPDFNumberTreeObjectHelper` to align with the new `NNTreeImpl` structure.
- Simplified methods accessing tree items and validating keys.
libqpdf/NNTree.cc
@@ -68,7 +68,7 @@ NNTreeIterator::updateIValue(bool allow_invalid) @@ -68,7 +68,7 @@ NNTreeIterator::updateIValue(bool allow_invalid)
68 ivalue.second = QPDFObjectHandle(); 68 ivalue.second = QPDFObjectHandle();
69 return; 69 return;
70 } 70 }
71 - Array items = node.getKey(impl.details.itemsKey()); 71 + Array items = node.getKey(impl.itemsKey());
72 if (!std::cmp_less(item_number + 1, items.size())) { 72 if (!std::cmp_less(item_number + 1, items.size())) {
73 impl.error(node, "update ivalue: items array is too short"); 73 impl.error(node, "update ivalue: items array is too short");
74 } 74 }
@@ -91,7 +91,7 @@ NNTreeIterator::getNextKid(PathElement& pe, bool backward) @@ -91,7 +91,7 @@ NNTreeIterator::getNextKid(PathElement& pe, bool backward)
91 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) { 91 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) {
92 auto result = kids[pe.kid_number]; 92 auto result = kids[pe.kid_number];
93 if (result.isDictionary() && 93 if (result.isDictionary() &&
94 - (result.hasKey("/Kids") || result.hasKey(impl.details.itemsKey()))) { 94 + (result.hasKey("/Kids") || result.hasKey(impl.itemsKey()))) {
95 return result; 95 return result;
96 } else { 96 } else {
97 impl.warn( 97 impl.warn(
@@ -121,7 +121,7 @@ NNTreeIterator::increment(bool backward) @@ -121,7 +121,7 @@ NNTreeIterator::increment(bool backward)
121 121
122 while (valid()) { 122 while (valid()) {
123 item_number += backward ? -2 : 2; 123 item_number += backward ? -2 : 2;
124 - Array items = node.getKey(impl.details.itemsKey()); 124 + Array items = node.getKey(impl.itemsKey());
125 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) { 125 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) {
126 bool found = false; 126 bool found = false;
127 setItemNumber(QPDFObjectHandle(), -1); 127 setItemNumber(QPDFObjectHandle(), -1);
@@ -136,10 +136,10 @@ NNTreeIterator::increment(bool backward) @@ -136,10 +136,10 @@ NNTreeIterator::increment(bool backward)
136 } 136 }
137 } 137 }
138 if (item_number >= 0) { 138 if (item_number >= 0) {
139 - items = node.getKey(impl.details.itemsKey()); 139 + items = node.getKey(impl.itemsKey());
140 if (std::cmp_greater_equal(item_number + 1, items.size())) { 140 if (std::cmp_greater_equal(item_number + 1, items.size())) {
141 impl.warn(node, "items array doesn't have enough elements"); 141 impl.warn(node, "items array doesn't have enough elements");
142 - } else if (!impl.details.keyValid(items[item_number])) { 142 + } else if (!impl.keyValid(items[item_number])) {
143 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type")); 143 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type"));
144 } else if (!impl.value_valid(items[item_number + 1])) { 144 } else if (!impl.value_valid(items[item_number + 1])) {
145 impl.warn(node, "item " + std::to_string(item_number + 1) + " is invalid"); 145 impl.warn(node, "item " + std::to_string(item_number + 1) + " is invalid");
@@ -160,7 +160,7 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -160,7 +160,7 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
160 } 160 }
161 Array kids = a_node.getKey("/Kids"); 161 Array kids = a_node.getKey("/Kids");
162 size_t nkids = kids.size(); 162 size_t nkids = kids.size();
163 - Array items = a_node.getKey(impl.details.itemsKey()); 163 + Array items = a_node.getKey(impl.itemsKey());
164 size_t nitems = items.size(); 164 size_t nitems = items.size();
165 165
166 bool changed = true; 166 bool changed = true;
@@ -187,9 +187,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -187,9 +187,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
187 if (olimits.size() == 2) { 187 if (olimits.size() == 2) {
188 auto ofirst = olimits[0]; 188 auto ofirst = olimits[0];
189 auto olast = olimits[1]; 189 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) { 190 + if (impl.keyValid(ofirst) && impl.keyValid(olast) &&
  191 + impl.compareKeys(first, ofirst) == 0 && impl.compareKeys(last, olast) == 0) {
193 changed = false; 192 changed = false;
194 } 193 }
195 } 194 }
@@ -246,7 +245,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -246,7 +245,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. 245 // Find the array we actually need to split, which is either this node's kids or items.
247 Array kids = to_split.getKey("/Kids"); 246 Array kids = to_split.getKey("/Kids");
248 size_t nkids = kids.size(); 247 size_t nkids = kids.size();
249 - Array items = to_split.getKey(impl.details.itemsKey()); 248 + Array items = to_split.getKey(impl.itemsKey());
250 size_t nitems = items.size(); 249 size_t nitems = items.size();
251 250
252 Array first_half; 251 Array first_half;
@@ -261,7 +260,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -261,7 +260,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
261 first_half = items; 260 first_half = items;
262 n = nitems; 261 n = nitems;
263 threshold *= 2; 262 threshold *= 2;
264 - key = impl.details.itemsKey(); 263 + key = impl.itemsKey();
265 } else { 264 } else {
266 throw std::logic_error("NNTreeIterator::split called on invalid node"); 265 throw std::logic_error("NNTreeIterator::split called on invalid node");
267 } 266 }
@@ -295,7 +294,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -295,7 +294,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
295 Array new_kids; 294 Array new_kids;
296 new_kids.push_back(first_node); 295 new_kids.push_back(first_node);
297 to_split.removeKey("/Limits"); // already shouldn't be there for root 296 to_split.removeKey("/Limits"); // already shouldn't be there for root
298 - to_split.removeKey(impl.details.itemsKey()); 297 + to_split.removeKey(impl.itemsKey());
299 to_split.replaceKey("/Kids", new_kids); 298 to_split.replaceKey("/Kids", new_kids);
300 if (is_leaf) { 299 if (is_leaf) {
301 node = first_node; 300 node = first_node;
@@ -375,7 +374,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; @@ -375,7 +374,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp;
375 return; 374 return;
376 } 375 }
377 376
378 - Array items = node.getKey(impl.details.itemsKey()); 377 + Array items = node.getKey(impl.itemsKey());
379 if (!items) { 378 if (!items) {
380 impl.error(node, "node contains no items array"); 379 impl.error(node, "node contains no items array");
381 } 380 }
@@ -404,7 +403,7 @@ NNTreeIterator::remove() @@ -404,7 +403,7 @@ NNTreeIterator::remove()
404 if (!valid()) { 403 if (!valid()) {
405 throw std::logic_error("attempt made to remove an invalid iterator"); 404 throw std::logic_error("attempt made to remove an invalid iterator");
406 } 405 }
407 - Array items = node.getKey(impl.details.itemsKey()); 406 + Array items = node.getKey(impl.itemsKey());
408 int nitems = static_cast<int>(items.size()); 407 int nitems = static_cast<int>(items.size());
409 if (std::cmp_greater(item_number + 2, nitems)) { 408 if (std::cmp_greater(item_number + 2, nitems)) {
410 impl.error(node, "found short items array while removing an item"); 409 impl.error(node, "found short items array while removing an item");
@@ -478,7 +477,7 @@ NNTreeIterator::remove() @@ -478,7 +477,7 @@ NNTreeIterator::remove()
478 if (parent == path.end()) { 477 if (parent == path.end()) {
479 // We erased the very last item. Convert the root to an empty items array. 478 // We erased the very last item. Convert the root to an empty items array.
480 element->node.removeKey("/Kids"); 479 element->node.removeKey("/Kids");
481 - element->node.replaceKey(impl.details.itemsKey(), Array()); 480 + element->node.replaceKey(impl.itemsKey(), Array());
482 path.clear(); 481 path.clear();
483 setItemNumber(impl.oh, -1); 482 setItemNumber(impl.oh, -1);
484 return; 483 return;
@@ -579,7 +578,7 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -579,7 +578,7 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
579 return fail(a_node, "non-dictionary node while traversing name/number tree"); 578 return fail(a_node, "non-dictionary node while traversing name/number tree");
580 } 579 }
581 580
582 - Array items = a_node.getKey(impl.details.itemsKey()); 581 + Array items = a_node.getKey(impl.itemsKey());
583 int nitems = static_cast<int>(items.size()); 582 int nitems = static_cast<int>(items.size());
584 if (nitems > 1) { 583 if (nitems > 1) {
585 setItemNumber(a_node, first ? 0 : nitems - 2); 584 setItemNumber(a_node, first ? 0 : nitems - 2);
@@ -616,22 +615,22 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -616,22 +615,22 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
616 } else { 615 } else {
617 return fail( 616 return fail(
618 a_node, 617 a_node,
619 - "name/number tree node has neither non-empty " + impl.details.itemsKey() +  
620 - " nor /Kids"); 618 + "name/number tree node has neither non-empty " + impl.itemsKey() + " nor /Kids");
621 } 619 }
622 } 620 }
623 return true; 621 return true;
624 } 622 }
625 623
626 NNTreeImpl::NNTreeImpl( 624 NNTreeImpl::NNTreeImpl(
627 - NNTreeDetails const& details,  
628 QPDF& qpdf, 625 QPDF& qpdf,
629 QPDFObjectHandle& oh, 626 QPDFObjectHandle& oh,
  627 + qpdf_object_type_e key_type,
630 std::function<bool(QPDFObjectHandle const&)> value_validator, 628 std::function<bool(QPDFObjectHandle const&)> value_validator,
631 bool auto_repair) : 629 bool auto_repair) :
632 - details(details),  
633 qpdf(qpdf), 630 qpdf(qpdf),
634 oh(oh), 631 oh(oh),
  632 + key_type(key_type),
  633 + items_key(key_type == ::ot_string ? "/Names" : "/Nums"),
635 value_valid(value_validator), 634 value_valid(value_validator),
636 auto_repair(auto_repair) 635 auto_repair(auto_repair)
637 { 636 {
@@ -666,16 +665,32 @@ NNTreeImpl::last() @@ -666,16 +665,32 @@ NNTreeImpl::last()
666 } 665 }
667 666
668 int 667 int
  668 +NNTreeImpl::compareKeys(QPDFObjectHandle a, QPDFObjectHandle b) const
  669 +{
  670 + // We don't call this without calling keyValid first
  671 + qpdf_assert_debug(keyValid(a));
  672 + qpdf_assert_debug(keyValid(b));
  673 + if (key_type == ::ot_string) {
  674 + auto as = a.getUTF8Value();
  675 + auto bs = b.getUTF8Value();
  676 + return as < bs ? -1 : (as > bs ? 1 : 0);
  677 + }
  678 + auto as = a.getIntValue();
  679 + auto bs = b.getIntValue();
  680 + return as < bs ? -1 : (as > bs ? 1 : 0);
  681 +}
  682 +
  683 +int
669 NNTreeImpl::withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node) 684 NNTreeImpl::withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node)
670 { 685 {
671 Array limits = node.getKey("/Limits"); 686 Array limits = node.getKey("/Limits");
672 - if (!(details.keyValid(limits[0]) && details.keyValid(limits[1]))) { 687 + if (!(keyValid(limits[0]) && keyValid(limits[1]))) {
673 error(node, "node is missing /Limits"); 688 error(node, "node is missing /Limits");
674 } 689 }
675 - if (details.compareKeys(key, limits[0]) < 0) { 690 + if (compareKeys(key, limits[0]) < 0) {
676 return -1; 691 return -1;
677 } 692 }
678 - if (details.compareKeys(key, limits[1]) > 0) { 693 + if (compareKeys(key, limits[1]) > 0) {
679 return 1; 694 return 1;
680 } 695 }
681 return 0; 696 return 0;
@@ -728,10 +743,10 @@ NNTreeImpl::binarySearch( @@ -728,10 +743,10 @@ NNTreeImpl::binarySearch(
728 int 743 int
729 NNTreeImpl::compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx) 744 NNTreeImpl::compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx)
730 { 745 {
731 - if (!(std::cmp_greater(items.size(), 2 * idx) && details.keyValid(items[2 * idx]))) { 746 + if (!(std::cmp_greater(items.size(), 2 * idx) && keyValid(items[2 * idx]))) {
732 error(oh, ("item at index " + std::to_string(2 * idx) + " is not the right type")); 747 error(oh, ("item at index " + std::to_string(2 * idx) + " is not the right type"));
733 } 748 }
734 - return details.compareKeys(key, items[2 * idx]); 749 + return compareKeys(key, items[2 * idx]);
735 } 750 }
736 751
737 int 752 int
@@ -747,15 +762,15 @@ void @@ -747,15 +762,15 @@ void
747 NNTreeImpl::repair() 762 NNTreeImpl::repair()
748 { 763 {
749 auto new_node = QPDFObjectHandle::newDictionary(); 764 auto new_node = QPDFObjectHandle::newDictionary();
750 - new_node.replaceKey(details.itemsKey(), Array());  
751 - NNTreeImpl repl(details, qpdf, new_node, value_valid, false); 765 + new_node.replaceKey(itemsKey(), Array());
  766 + NNTreeImpl repl(qpdf, new_node, key_type, value_valid, false);
752 for (auto const& [key, value]: *this) { 767 for (auto const& [key, value]: *this) {
753 if (key && value) { 768 if (key && value) {
754 repl.insert(key, value); 769 repl.insert(key, value);
755 } 770 }
756 } 771 }
757 oh.replaceKey("/Kids", new_node.getKey("/Kids")); 772 oh.replaceKey("/Kids", new_node.getKey("/Kids"));
758 - oh.replaceKey(details.itemsKey(), new_node.getKey(details.itemsKey())); 773 + oh.replaceKey(itemsKey(), new_node.getKey(itemsKey()));
759 } 774 }
760 775
761 NNTreeImpl::iterator 776 NNTreeImpl::iterator
@@ -783,13 +798,13 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo @@ -783,13 +798,13 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
783 return end(); 798 return end();
784 } 799 }
785 if (first_item.valid()) { 800 if (first_item.valid()) {
786 - if (!details.keyValid(first_item->first)) { 801 + if (!keyValid(first_item->first)) {
787 error(oh, "encountered invalid key in find"); 802 error(oh, "encountered invalid key in find");
788 } 803 }
789 if (!value_valid(first_item->second)) { 804 if (!value_valid(first_item->second)) {
790 error(oh, "encountered invalid value in find"); 805 error(oh, "encountered invalid value in find");
791 } 806 }
792 - if (details.compareKeys(key, first_item->first) < 0) { 807 + if (compareKeys(key, first_item->first) < 0) {
793 // Before the first key 808 // Before the first key
794 return end(); 809 return end();
795 } 810 }
@@ -805,14 +820,14 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo @@ -805,14 +820,14 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
805 error(node, "loop detected in find"); 820 error(node, "loop detected in find");
806 } 821 }
807 822
808 - Array items = node.getKey(details.itemsKey()); 823 + Array items = node.getKey(itemsKey());
809 size_t nitems = items.size(); 824 size_t nitems = items.size();
810 if (nitems > 1) { 825 if (nitems > 1) {
811 int idx = binarySearch( 826 int idx = binarySearch(
812 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); 827 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem);
813 if (idx >= 0) { 828 if (idx >= 0) {
814 result.setItemNumber(node, 2 * idx); 829 result.setItemNumber(node, 2 * idx);
815 - if (!result.impl.details.keyValid(result.ivalue.first)) { 830 + if (!result.impl.keyValid(result.ivalue.first)) {
816 error(node, "encountered invalid key in find"); 831 error(node, "encountered invalid key in find");
817 } 832 }
818 if (!result.impl.value_valid(result.ivalue.second)) { 833 if (!result.impl.value_valid(result.ivalue.second)) {
@@ -843,7 +858,7 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val @@ -843,7 +858,7 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val
843 auto iter = begin(); 858 auto iter = begin();
844 Array items(nullptr); 859 Array items(nullptr);
845 if (iter.node.isDictionary()) { 860 if (iter.node.isDictionary()) {
846 - items = iter.node.getKey(details.itemsKey()); 861 + items = iter.node.getKey(itemsKey());
847 } 862 }
848 if (!items) { 863 if (!items) {
849 error(oh, "unable to find a valid items node"); 864 error(oh, "unable to find a valid items node");
@@ -868,8 +883,8 @@ NNTreeImpl::insert(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; value) @@ -868,8 +883,8 @@ NNTreeImpl::insert(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; value)
868 auto iter = find(key, true); 883 auto iter = find(key, true);
869 if (!iter.valid()) { 884 if (!iter.valid()) {
870 return insertFirst(key, value); 885 return insertFirst(key, value);
871 - } else if (details.compareKeys(key, iter->first) == 0) {  
872 - Array items = iter.node.getKey(details.itemsKey()); 886 + } else if (compareKeys(key, iter->first) == 0) {
  887 + Array items = iter.node.getKey(itemsKey());
873 items.set(iter.item_number + 1, value); 888 items.set(iter.item_number + 1, value);
874 iter.updateIValue(); 889 iter.updateIValue();
875 } else { 890 } else {
@@ -899,7 +914,7 @@ NNTreeImpl::validate(bool a_repair) @@ -899,7 +914,7 @@ NNTreeImpl::validate(bool a_repair)
899 QPDFObjectHandle last_key; 914 QPDFObjectHandle last_key;
900 try { 915 try {
901 for (auto const& [key, value]: *this) { 916 for (auto const& [key, value]: *this) {
902 - if (!details.keyValid(key)) { 917 + if (!keyValid(key)) {
903 error(oh, "invalid key in validate"); 918 error(oh, "invalid key in validate");
904 } 919 }
905 if (!value_valid(value)) { 920 if (!value_valid(value)) {
@@ -907,7 +922,7 @@ NNTreeImpl::validate(bool a_repair) @@ -907,7 +922,7 @@ NNTreeImpl::validate(bool a_repair)
907 } 922 }
908 if (first) { 923 if (first) {
909 first = false; 924 first = false;
910 - } else if (last_key && details.compareKeys(last_key, key) != -1) { 925 + } else if (last_key && compareKeys(last_key, key) != -1) {
911 error(oh, "keys are not sorted in validate"); 926 error(oh, "keys are not sorted in validate");
912 } 927 }
913 last_key = key; 928 last_key = key;
libqpdf/QPDFNameTreeObjectHelper.cc
@@ -2,38 +2,6 @@ @@ -2,38 +2,6 @@
2 2
3 #include <qpdf/NNTree.hh> 3 #include <qpdf/NNTree.hh>
4 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) 5 QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() // NOLINT (modernize-use-equals-default)
38 { 6 {
39 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific 7 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific
@@ -45,7 +13,7 @@ QPDFNameTreeObjectHelper::Members::Members( @@ -45,7 +13,7 @@ QPDFNameTreeObjectHelper::Members::Members(
45 QPDF& q, 13 QPDF& q,
46 std::function<bool(QPDFObjectHandle const&)> value_validator, 14 std::function<bool(QPDFObjectHandle const&)> value_validator,
47 bool auto_repair) : 15 bool auto_repair) :
48 - impl(std::make_shared<NNTreeImpl>(name_tree_details, q, oh, value_validator, auto_repair)) 16 + impl(std::make_shared<NNTreeImpl>(q, oh, ::ot_string, value_validator, auto_repair))
49 { 17 {
50 } 18 }
51 19
libqpdf/QPDFNumberTreeObjectHelper.cc
@@ -3,38 +3,6 @@ @@ -3,38 +3,6 @@
3 #include <qpdf/NNTree.hh> 3 #include <qpdf/NNTree.hh>
4 #include <qpdf/QIntC.hh> 4 #include <qpdf/QIntC.hh>
5 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) 6 QPDFNumberTreeObjectHelper::~QPDFNumberTreeObjectHelper() // NOLINT (modernize-use-equals-default)
39 { 7 {
40 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific 8 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific
@@ -46,7 +14,7 @@ QPDFNumberTreeObjectHelper::Members::Members( @@ -46,7 +14,7 @@ QPDFNumberTreeObjectHelper::Members::Members(
46 QPDF& q, 14 QPDF& q,
47 std::function<bool(QPDFObjectHandle const&)> value_validator, 15 std::function<bool(QPDFObjectHandle const&)> value_validator,
48 bool auto_repair) : 16 bool auto_repair) :
49 - impl(std::make_shared<NNTreeImpl>(number_tree_details, q, oh, value_validator, auto_repair)) 17 + impl(std::make_shared<NNTreeImpl>(q, oh, ::ot_integer, value_validator, auto_repair))
50 { 18 {
51 } 19 }
52 20
libqpdf/qpdf/NNTree.hh
@@ -2,20 +2,12 @@ @@ -2,20 +2,12 @@
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
21 { 13 {
@@ -96,9 +88,9 @@ class NNTreeImpl @@ -96,9 +88,9 @@ class NNTreeImpl
96 typedef NNTreeIterator iterator; 88 typedef NNTreeIterator iterator;
97 89
98 NNTreeImpl( 90 NNTreeImpl(
99 - NNTreeDetails const&,  
100 QPDF&, 91 QPDF&,
101 QPDFObjectHandle&, 92 QPDFObjectHandle&,
  93 + qpdf_object_type_e key_type,
102 std::function<bool(QPDFObjectHandle const&)> value_validator, 94 std::function<bool(QPDFObjectHandle const&)> value_validator,
103 bool auto_repair = true); 95 bool auto_repair = true);
104 iterator begin(); 96 iterator begin();
@@ -130,10 +122,24 @@ class NNTreeImpl @@ -130,10 +122,24 @@ class NNTreeImpl
130 void warn(QPDFObjectHandle const& node, std::string const& msg); 122 void warn(QPDFObjectHandle const& node, std::string const& msg);
131 void error(QPDFObjectHandle const& node, std::string const& msg); 123 void error(QPDFObjectHandle const& node, std::string const& msg);
132 124
133 - NNTreeDetails const& details; 125 + std::string const&
  126 + itemsKey() const
  127 + {
  128 + return items_key;
  129 + }
  130 + bool
  131 + keyValid(QPDFObjectHandle o) const
  132 + {
  133 + return o.resolved_type_code() == key_type;
  134 + }
  135 + int
  136 + compareKeys(QPDFObjectHandle a, QPDFObjectHandle b) const;
  137 +
134 QPDF& qpdf; 138 QPDF& qpdf;
135 int split_threshold{32}; 139 int split_threshold{32};
136 QPDFObjectHandle oh; 140 QPDFObjectHandle oh;
  141 + const qpdf_object_type_e key_type;
  142 + const std::string items_key;
137 const std::function<bool(QPDFObjectHandle const&)> value_valid; 143 const std::function<bool(QPDFObjectHandle const&)> value_valid;
138 bool auto_repair{true}; 144 bool auto_repair{true};
139 size_t error_count{0}; 145 size_t error_count{0};