Commit 8ed3e8c79b5cbccfeccee865e555b68025ee2c1f
1 parent
e7e20772
NNTree: rework iterators to be more memory efficient
Keep a std::pair internal to the iterators so that operator* can return a reference and operator-> can work, and each can work without copying pairs of objects around.
Showing
8 changed files
with
228 additions
and
111 deletions
include/qpdf/QPDFNameTreeObjectHelper.hh
| ... | ... | @@ -75,9 +75,6 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper |
| 75 | 75 | |
| 76 | 76 | class iterator: public std::iterator< |
| 77 | 77 | std::bidirectional_iterator_tag, |
| 78 | - std::pair<std::string, QPDFObjectHandle>, | |
| 79 | - void, | |
| 80 | - std::pair<std::string, QPDFObjectHandle>*, | |
| 81 | 78 | std::pair<std::string, QPDFObjectHandle>> |
| 82 | 79 | { |
| 83 | 80 | friend class QPDFNameTreeObjectHelper; |
| ... | ... | @@ -105,6 +102,8 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper |
| 105 | 102 | QPDF_DLL |
| 106 | 103 | reference operator*(); |
| 107 | 104 | QPDF_DLL |
| 105 | + pointer operator->(); | |
| 106 | + QPDF_DLL | |
| 108 | 107 | bool operator==(iterator const& other) const; |
| 109 | 108 | QPDF_DLL |
| 110 | 109 | bool operator!=(iterator const& other) const |
| ... | ... | @@ -131,8 +130,11 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper |
| 131 | 130 | void remove(); |
| 132 | 131 | |
| 133 | 132 | private: |
| 133 | + void updateIValue(); | |
| 134 | + | |
| 134 | 135 | iterator(std::shared_ptr<NNTreeIterator> const&); |
| 135 | 136 | std::shared_ptr<NNTreeIterator> impl; |
| 137 | + value_type ivalue; | |
| 136 | 138 | }; |
| 137 | 139 | |
| 138 | 140 | // The iterator looks like map iterator, so i.first is a string | ... | ... |
include/qpdf/QPDFNumberTreeObjectHelper.hh
| ... | ... | @@ -94,9 +94,6 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper |
| 94 | 94 | |
| 95 | 95 | class iterator: public std::iterator< |
| 96 | 96 | std::bidirectional_iterator_tag, |
| 97 | - std::pair<numtree_number, QPDFObjectHandle>, | |
| 98 | - void, | |
| 99 | - std::pair<numtree_number, QPDFObjectHandle>*, | |
| 100 | 97 | std::pair<numtree_number, QPDFObjectHandle>> |
| 101 | 98 | { |
| 102 | 99 | friend class QPDFNumberTreeObjectHelper; |
| ... | ... | @@ -124,6 +121,8 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper |
| 124 | 121 | QPDF_DLL |
| 125 | 122 | reference operator*(); |
| 126 | 123 | QPDF_DLL |
| 124 | + pointer operator->(); | |
| 125 | + QPDF_DLL | |
| 127 | 126 | bool operator==(iterator const& other) const; |
| 128 | 127 | QPDF_DLL |
| 129 | 128 | bool operator!=(iterator const& other) const |
| ... | ... | @@ -150,8 +149,11 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper |
| 150 | 149 | void remove(); |
| 151 | 150 | |
| 152 | 151 | private: |
| 152 | + void updateIValue(); | |
| 153 | + | |
| 153 | 154 | iterator(std::shared_ptr<NNTreeIterator> const&); |
| 154 | 155 | std::shared_ptr<NNTreeIterator> impl; |
| 156 | + value_type ivalue; | |
| 155 | 157 | }; |
| 156 | 158 | |
| 157 | 159 | // The iterator looks like map iterator, so i.first is a string | ... | ... |
libqpdf/NNTree.cc
| ... | ... | @@ -50,6 +50,57 @@ NNTreeIterator::NNTreeIterator(NNTreeImpl& impl) : |
| 50 | 50 | { |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | +void | |
| 54 | +NNTreeIterator::updateIValue(bool allow_invalid) | |
| 55 | +{ | |
| 56 | + // ivalue should never be used inside the class since we return a | |
| 57 | + // pointer/reference to it. Every bit of code that ever changes | |
| 58 | + // what object the iterator points to should take care to call | |
| 59 | + // updateIValue. Failure to do this means that any old references | |
| 60 | + // to *iter will point to incorrect objects, though the next | |
| 61 | + // dereference of the iterator will fix it. This isn't necessarily | |
| 62 | + // catastrophic, but it would be confusing. The test suite | |
| 63 | + // attempts to exercise various cases to ensure we don't introduce | |
| 64 | + // that bug in the future, but sadly it's tricky to verify by | |
| 65 | + // reasoning about the code that this constraint is always | |
| 66 | + // satisfied. Whenever we update what the iterator points to, we | |
| 67 | + // should call setItemNumber, which calls this. If we change what | |
| 68 | + // the iterator in some other way, such as replacing a value or | |
| 69 | + // removing an item and making the iterator point at a different | |
| 70 | + // item in potentially the same position, we must call | |
| 71 | + // updateIValue as well. These cases are handled, and for good | |
| 72 | + // measure, we also call updateIValue in operator* and operator->. | |
| 73 | + | |
| 74 | + bool okay = false; | |
| 75 | + if ((item_number >= 0) && | |
| 76 | + this->node.isInitialized() && | |
| 77 | + this->node.isDictionary()) | |
| 78 | + { | |
| 79 | + auto items = this->node.getKey(impl.details.itemsKey()); | |
| 80 | + if (this->item_number + 1 < items.getArrayNItems()) | |
| 81 | + { | |
| 82 | + okay = true; | |
| 83 | + this->ivalue.first = items.getArrayItem(this->item_number); | |
| 84 | + this->ivalue.second = items.getArrayItem(1+this->item_number); | |
| 85 | + } | |
| 86 | + else | |
| 87 | + { | |
| 88 | + error(impl.qpdf, node, "update ivalue: items array is too short"); | |
| 89 | + } | |
| 90 | + } | |
| 91 | + if (! okay) | |
| 92 | + { | |
| 93 | + if (! allow_invalid) | |
| 94 | + { | |
| 95 | + throw std::logic_error( | |
| 96 | + "attempt made to dereference an invalid" | |
| 97 | + " name/number tree iterator"); | |
| 98 | + } | |
| 99 | + this->ivalue.first = QPDFObjectHandle(); | |
| 100 | + this->ivalue.second = QPDFObjectHandle(); | |
| 101 | + } | |
| 102 | +} | |
| 103 | + | |
| 53 | 104 | NNTreeIterator::PathElement::PathElement( |
| 54 | 105 | QPDFObjectHandle const& node, int kid_number) : |
| 55 | 106 | node(node), |
| ... | ... | @@ -522,6 +573,7 @@ NNTreeIterator::remove() |
| 522 | 573 | // We don't have to do anything since the removed item's |
| 523 | 574 | // successor now occupies its former location. |
| 524 | 575 | QTC::TC("qpdf", "NNTree erased non-last item"); |
| 576 | + updateIValue(); | |
| 525 | 577 | } |
| 526 | 578 | else |
| 527 | 579 | { |
| ... | ... | @@ -630,19 +682,15 @@ NNTreeIterator::operator--() |
| 630 | 682 | NNTreeIterator::reference |
| 631 | 683 | NNTreeIterator::operator*() |
| 632 | 684 | { |
| 633 | - if (this->item_number < 0) | |
| 634 | - { | |
| 635 | - throw std::logic_error( | |
| 636 | - "attempt made to dereference an invalid" | |
| 637 | - " name/number tree iterator"); | |
| 638 | - } | |
| 639 | - auto items = this->node.getKey(impl.details.itemsKey()); | |
| 640 | - if (items.getArrayNItems() < this->item_number + 2) | |
| 641 | - { | |
| 642 | - error(impl.qpdf, node, "operator*: items array is too short"); | |
| 643 | - } | |
| 644 | - return std::make_pair(items.getArrayItem(this->item_number), | |
| 645 | - items.getArrayItem(1+this->item_number)); | |
| 685 | + updateIValue(false); | |
| 686 | + return this->ivalue; | |
| 687 | +} | |
| 688 | + | |
| 689 | +NNTreeIterator::pointer | |
| 690 | +NNTreeIterator::operator->() | |
| 691 | +{ | |
| 692 | + updateIValue(false); | |
| 693 | + return &(this->ivalue); | |
| 646 | 694 | } |
| 647 | 695 | |
| 648 | 696 | bool |
| ... | ... | @@ -660,7 +708,7 @@ NNTreeIterator::operator==(NNTreeIterator const& other) const |
| 660 | 708 | auto opi = other.path.begin(); |
| 661 | 709 | while (tpi != this->path.end()) |
| 662 | 710 | { |
| 663 | - if ((*tpi).kid_number != (*opi).kid_number) | |
| 711 | + if (tpi->kid_number != opi->kid_number) | |
| 664 | 712 | { |
| 665 | 713 | return false; |
| 666 | 714 | } |
| ... | ... | @@ -679,6 +727,7 @@ NNTreeIterator::setItemNumber(QPDFObjectHandle const& node, int n) |
| 679 | 727 | { |
| 680 | 728 | this->node = node; |
| 681 | 729 | this->item_number = n; |
| 730 | + updateIValue(); | |
| 682 | 731 | } |
| 683 | 732 | |
| 684 | 733 | void |
| ... | ... | @@ -961,7 +1010,7 @@ NNTreeImpl::repair() |
| 961 | 1010 | auto new_node = QPDFObjectHandle::newDictionary(); |
| 962 | 1011 | new_node.replaceKey(details.itemsKey(), QPDFObjectHandle::newArray()); |
| 963 | 1012 | NNTreeImpl repl(details, qpdf, new_node, false); |
| 964 | - for (auto i: *this) | |
| 1013 | + for (auto const& i: *this) | |
| 965 | 1014 | { |
| 966 | 1015 | repl.insert(i.first, i.second); |
| 967 | 1016 | } |
| ... | ... | @@ -1005,15 +1054,15 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) |
| 1005 | 1054 | return end(); |
| 1006 | 1055 | } |
| 1007 | 1056 | else if (first_item.valid() && |
| 1008 | - details.keyValid((*first_item).first) && | |
| 1009 | - details.compareKeys(key, (*first_item).first) < 0) | |
| 1057 | + details.keyValid(first_item->first) && | |
| 1058 | + details.compareKeys(key, first_item->first) < 0) | |
| 1010 | 1059 | { |
| 1011 | 1060 | // Before the first key |
| 1012 | 1061 | return end(); |
| 1013 | 1062 | } |
| 1014 | 1063 | else if (last_item.valid() && |
| 1015 | - details.keyValid((*last_item).first) && | |
| 1016 | - details.compareKeys(key, (*last_item).first) > 0) | |
| 1064 | + details.keyValid(last_item->first) && | |
| 1065 | + details.compareKeys(key, last_item->first) > 0) | |
| 1017 | 1066 | { |
| 1018 | 1067 | // After the last key |
| 1019 | 1068 | if (return_prev_if_not_found) |
| ... | ... | @@ -1097,10 +1146,10 @@ NNTreeImpl::insertFirst(QPDFObjectHandle key, QPDFObjectHandle value) |
| 1097 | 1146 | } |
| 1098 | 1147 | items.insertItem(0, key); |
| 1099 | 1148 | items.insertItem(1, value); |
| 1100 | - iter.item_number = 0; | |
| 1149 | + iter.setItemNumber(iter.node, 0); | |
| 1101 | 1150 | iter.resetLimits(iter.node, iter.lastPathElement()); |
| 1102 | 1151 | iter.split(iter.node, iter.lastPathElement()); |
| 1103 | - return begin(); | |
| 1152 | + return iter; | |
| 1104 | 1153 | } |
| 1105 | 1154 | |
| 1106 | 1155 | NNTreeImpl::iterator |
| ... | ... | @@ -1112,11 +1161,12 @@ NNTreeImpl::insert(QPDFObjectHandle key, QPDFObjectHandle value) |
| 1112 | 1161 | QTC::TC("qpdf", "NNTree insert inserts first"); |
| 1113 | 1162 | return insertFirst(key, value); |
| 1114 | 1163 | } |
| 1115 | - else if (details.compareKeys(key, (*iter).first) == 0) | |
| 1164 | + else if (details.compareKeys(key, iter->first) == 0) | |
| 1116 | 1165 | { |
| 1117 | 1166 | QTC::TC("qpdf", "NNTree insert replaces"); |
| 1118 | 1167 | auto items = iter.node.getKey(details.itemsKey()); |
| 1119 | 1168 | items.setArrayItem(iter.item_number + 1, value); |
| 1169 | + iter.updateIValue(); | |
| 1120 | 1170 | } |
| 1121 | 1171 | else |
| 1122 | 1172 | { |
| ... | ... | @@ -1137,7 +1187,7 @@ NNTreeImpl::remove(QPDFObjectHandle key, QPDFObjectHandle* value) |
| 1137 | 1187 | } |
| 1138 | 1188 | if (value) |
| 1139 | 1189 | { |
| 1140 | - *value = (*iter).second; | |
| 1190 | + *value = iter->second; | |
| 1141 | 1191 | } |
| 1142 | 1192 | iter.remove(); |
| 1143 | 1193 | return true; | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -3011,7 +3011,7 @@ QPDF::findAttachmentStreams() |
| 3011 | 3011 | return; |
| 3012 | 3012 | } |
| 3013 | 3013 | QPDFNameTreeObjectHelper ef_tree(embedded_files, *this); |
| 3014 | - for (auto i: ef_tree) | |
| 3014 | + for (auto const& i: ef_tree) | |
| 3015 | 3015 | { |
| 3016 | 3016 | QPDFObjectHandle item = i.second; |
| 3017 | 3017 | if (item.isDictionary() && | ... | ... |
libqpdf/QPDFNameTreeObjectHelper.cc
| ... | ... | @@ -79,6 +79,7 @@ QPDFNameTreeObjectHelper::iterator& |
| 79 | 79 | QPDFNameTreeObjectHelper::iterator::operator++() |
| 80 | 80 | { |
| 81 | 81 | ++(*impl); |
| 82 | + updateIValue(); | |
| 82 | 83 | return *this; |
| 83 | 84 | } |
| 84 | 85 | |
| ... | ... | @@ -86,14 +87,38 @@ QPDFNameTreeObjectHelper::iterator& |
| 86 | 87 | QPDFNameTreeObjectHelper::iterator::operator--() |
| 87 | 88 | { |
| 88 | 89 | --(*impl); |
| 90 | + updateIValue(); | |
| 89 | 91 | return *this; |
| 90 | 92 | } |
| 91 | 93 | |
| 94 | +void | |
| 95 | +QPDFNameTreeObjectHelper::iterator::updateIValue() | |
| 96 | +{ | |
| 97 | + if (impl->valid()) | |
| 98 | + { | |
| 99 | + auto p = *impl; | |
| 100 | + this->ivalue.first = p->first.getUTF8Value(); | |
| 101 | + this->ivalue.second = p->second; | |
| 102 | + } | |
| 103 | + else | |
| 104 | + { | |
| 105 | + this->ivalue.first = ""; | |
| 106 | + this->ivalue.second = QPDFObjectHandle(); | |
| 107 | + } | |
| 108 | +} | |
| 109 | + | |
| 92 | 110 | QPDFNameTreeObjectHelper::iterator::reference |
| 93 | 111 | QPDFNameTreeObjectHelper::iterator::operator*() |
| 94 | 112 | { |
| 95 | - auto p = **impl; | |
| 96 | - return std::make_pair(p.first.getUTF8Value(), p.second); | |
| 113 | + updateIValue(); | |
| 114 | + return this->ivalue; | |
| 115 | +} | |
| 116 | + | |
| 117 | +QPDFNameTreeObjectHelper::iterator::pointer | |
| 118 | +QPDFNameTreeObjectHelper::iterator::operator->() | |
| 119 | +{ | |
| 120 | + updateIValue(); | |
| 121 | + return &this->ivalue; | |
| 97 | 122 | } |
| 98 | 123 | |
| 99 | 124 | bool |
| ... | ... | @@ -107,12 +132,14 @@ QPDFNameTreeObjectHelper::iterator::insertAfter( |
| 107 | 132 | std::string const& key, QPDFObjectHandle value) |
| 108 | 133 | { |
| 109 | 134 | impl->insertAfter(QPDFObjectHandle::newUnicodeString(key), value); |
| 135 | + updateIValue(); | |
| 110 | 136 | } |
| 111 | 137 | |
| 112 | 138 | void |
| 113 | 139 | QPDFNameTreeObjectHelper::iterator::remove() |
| 114 | 140 | { |
| 115 | 141 | impl->remove(); |
| 142 | + updateIValue(); | |
| 116 | 143 | } |
| 117 | 144 | |
| 118 | 145 | QPDFNameTreeObjectHelper::iterator |
| ... | ... | @@ -175,7 +202,7 @@ QPDFNameTreeObjectHelper::findObject( |
| 175 | 202 | { |
| 176 | 203 | return false; |
| 177 | 204 | } |
| 178 | - oh = (*i).second; | |
| 205 | + oh = i->second; | |
| 179 | 206 | return true; |
| 180 | 207 | } |
| 181 | 208 | ... | ... |
libqpdf/QPDFNumberTreeObjectHelper.cc
| ... | ... | @@ -75,6 +75,7 @@ QPDFNumberTreeObjectHelper::iterator& |
| 75 | 75 | QPDFNumberTreeObjectHelper::iterator::operator++() |
| 76 | 76 | { |
| 77 | 77 | ++(*impl); |
| 78 | + updateIValue(); | |
| 78 | 79 | return *this; |
| 79 | 80 | } |
| 80 | 81 | |
| ... | ... | @@ -82,14 +83,38 @@ QPDFNumberTreeObjectHelper::iterator& |
| 82 | 83 | QPDFNumberTreeObjectHelper::iterator::operator--() |
| 83 | 84 | { |
| 84 | 85 | --(*impl); |
| 86 | + updateIValue(); | |
| 85 | 87 | return *this; |
| 86 | 88 | } |
| 87 | 89 | |
| 90 | +void | |
| 91 | +QPDFNumberTreeObjectHelper::iterator::updateIValue() | |
| 92 | +{ | |
| 93 | + if (impl->valid()) | |
| 94 | + { | |
| 95 | + auto p = *impl; | |
| 96 | + this->ivalue.first = p->first.getIntValue(); | |
| 97 | + this->ivalue.second = p->second; | |
| 98 | + } | |
| 99 | + else | |
| 100 | + { | |
| 101 | + this->ivalue.first = 0; | |
| 102 | + this->ivalue.second = QPDFObjectHandle(); | |
| 103 | + } | |
| 104 | +} | |
| 105 | + | |
| 88 | 106 | QPDFNumberTreeObjectHelper::iterator::reference |
| 89 | 107 | QPDFNumberTreeObjectHelper::iterator::operator*() |
| 90 | 108 | { |
| 91 | - auto p = **impl; | |
| 92 | - return std::make_pair(p.first.getIntValue(), p.second); | |
| 109 | + updateIValue(); | |
| 110 | + return this->ivalue; | |
| 111 | +} | |
| 112 | + | |
| 113 | +QPDFNumberTreeObjectHelper::iterator::pointer | |
| 114 | +QPDFNumberTreeObjectHelper::iterator::operator->() | |
| 115 | +{ | |
| 116 | + updateIValue(); | |
| 117 | + return &this->ivalue; | |
| 93 | 118 | } |
| 94 | 119 | |
| 95 | 120 | bool |
| ... | ... | @@ -103,12 +128,14 @@ QPDFNumberTreeObjectHelper::iterator::insertAfter( |
| 103 | 128 | numtree_number key, QPDFObjectHandle value) |
| 104 | 129 | { |
| 105 | 130 | impl->insertAfter(QPDFObjectHandle::newInteger(key), value); |
| 131 | + updateIValue(); | |
| 106 | 132 | } |
| 107 | 133 | |
| 108 | 134 | void |
| 109 | 135 | QPDFNumberTreeObjectHelper::iterator::remove() |
| 110 | 136 | { |
| 111 | 137 | impl->remove(); |
| 138 | + updateIValue(); | |
| 112 | 139 | } |
| 113 | 140 | |
| 114 | 141 | QPDFNumberTreeObjectHelper::iterator |
| ... | ... | @@ -162,7 +189,7 @@ QPDFNumberTreeObjectHelper::getMin() |
| 162 | 189 | { |
| 163 | 190 | return 0; |
| 164 | 191 | } |
| 165 | - return (*i).first; | |
| 192 | + return i->first; | |
| 166 | 193 | } |
| 167 | 194 | |
| 168 | 195 | QPDFNumberTreeObjectHelper::numtree_number |
| ... | ... | @@ -173,7 +200,7 @@ QPDFNumberTreeObjectHelper::getMax() |
| 173 | 200 | { |
| 174 | 201 | return 0; |
| 175 | 202 | } |
| 176 | - return (*i).first; | |
| 203 | + return i->first; | |
| 177 | 204 | } |
| 178 | 205 | |
| 179 | 206 | bool |
| ... | ... | @@ -192,7 +219,7 @@ QPDFNumberTreeObjectHelper::findObject( |
| 192 | 219 | { |
| 193 | 220 | return false; |
| 194 | 221 | } |
| 195 | - oh = (*i).second; | |
| 222 | + oh = i->second; | |
| 196 | 223 | return true; |
| 197 | 224 | } |
| 198 | 225 | |
| ... | ... | @@ -206,8 +233,8 @@ QPDFNumberTreeObjectHelper::findObjectAtOrBelow( |
| 206 | 233 | { |
| 207 | 234 | return false; |
| 208 | 235 | } |
| 209 | - oh = (*i).second; | |
| 210 | - offset = idx - (*i).first; | |
| 236 | + oh = i->second; | |
| 237 | + offset = idx - i->first; | |
| 211 | 238 | return true; |
| 212 | 239 | } |
| 213 | 240 | ... | ... |
libqpdf/qpdf/NNTree.hh
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | |
| 7 | 7 | #include <iterator> |
| 8 | 8 | #include <list> |
| 9 | +#include <memory> | |
| 9 | 10 | |
| 10 | 11 | class NNTreeDetails |
| 11 | 12 | { |
| ... | ... | @@ -18,9 +19,6 @@ class NNTreeDetails |
| 18 | 19 | class NNTreeImpl; |
| 19 | 20 | class NNTreeIterator: public std::iterator< |
| 20 | 21 | std::bidirectional_iterator_tag, |
| 21 | - std::pair<QPDFObjectHandle, QPDFObjectHandle>, | |
| 22 | - void, | |
| 23 | - std::pair<QPDFObjectHandle, QPDFObjectHandle>*, | |
| 24 | 22 | std::pair<QPDFObjectHandle, QPDFObjectHandle>> |
| 25 | 23 | { |
| 26 | 24 | friend class NNTreeImpl; |
| ... | ... | @@ -41,6 +39,7 @@ class NNTreeIterator: public std::iterator< |
| 41 | 39 | return t; |
| 42 | 40 | } |
| 43 | 41 | reference operator*(); |
| 42 | + pointer operator->(); | |
| 44 | 43 | bool operator==(NNTreeIterator const& other) const; |
| 45 | 44 | bool operator!=(NNTreeIterator const& other) const |
| 46 | 45 | { |
| ... | ... | @@ -63,6 +62,7 @@ class NNTreeIterator: public std::iterator< |
| 63 | 62 | |
| 64 | 63 | // ABI: for qpdf 11, make qpdf a reference |
| 65 | 64 | NNTreeIterator(NNTreeImpl& impl); |
| 65 | + void updateIValue(bool allow_invalid = true); | |
| 66 | 66 | bool deepen(QPDFObjectHandle node, bool first, bool allow_empty); |
| 67 | 67 | void setItemNumber(QPDFObjectHandle const& node, int); |
| 68 | 68 | void addPathElement(QPDFObjectHandle const& node, int kid_number); |
| ... | ... | @@ -79,6 +79,7 @@ class NNTreeIterator: public std::iterator< |
| 79 | 79 | std::list<PathElement> path; |
| 80 | 80 | QPDFObjectHandle node; |
| 81 | 81 | int item_number; |
| 82 | + value_type ivalue; | |
| 82 | 83 | }; |
| 83 | 84 | |
| 84 | 85 | class NNTreeImpl | ... | ... |
qpdf/test_driver.cc
| ... | ... | @@ -1749,7 +1749,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1749 | 1749 | // number-tree.pdf |
| 1750 | 1750 | QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); |
| 1751 | 1751 | QPDFNumberTreeObjectHelper ntoh(qtest, pdf); |
| 1752 | - for (auto iter: ntoh) | |
| 1752 | + for (auto& iter: ntoh) | |
| 1753 | 1753 | { |
| 1754 | 1754 | std::cout << iter.first << " " |
| 1755 | 1755 | << iter.second.getStringValue() |
| ... | ... | @@ -1785,32 +1785,36 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1785 | 1785 | assert(iter1 == new1.end()); |
| 1786 | 1786 | new1.insert(1, QPDFObjectHandle::newString("1")); |
| 1787 | 1787 | ++iter1; |
| 1788 | - assert((*iter1).first == 1); | |
| 1788 | + assert((*iter1).first == 1); // exercise operator* explicitly | |
| 1789 | + auto& iter1_val = *iter1; | |
| 1789 | 1790 | --iter1; |
| 1790 | 1791 | assert(iter1 == new1.end()); |
| 1791 | 1792 | --iter1; |
| 1792 | - assert((*iter1).first == 1); | |
| 1793 | + assert(iter1->first == 1); | |
| 1794 | + assert(iter1_val.first == 1); | |
| 1793 | 1795 | new1.insert(2, QPDFObjectHandle::newString("2")); |
| 1794 | 1796 | ++iter1; |
| 1795 | - assert((*iter1).first == 2); | |
| 1797 | + assert(iter1->first == 2); | |
| 1798 | + assert(iter1_val.first == 2); | |
| 1796 | 1799 | ++iter1; |
| 1797 | 1800 | assert(iter1 == new1.end()); |
| 1801 | + assert(! iter1_val.second.isInitialized()); | |
| 1798 | 1802 | ++iter1; |
| 1799 | - assert((*iter1).first == 1); | |
| 1803 | + assert(iter1->first == 1); | |
| 1800 | 1804 | --iter1; |
| 1801 | 1805 | assert(iter1 == new1.end()); |
| 1802 | 1806 | --iter1; |
| 1803 | - assert((*iter1).first == 2); | |
| 1807 | + assert(iter1->first == 2); | |
| 1804 | 1808 | |
| 1805 | 1809 | std::cout << "insertAfter" << std::endl; |
| 1806 | 1810 | auto new2 = QPDFNumberTreeObjectHelper::newEmpty(pdf); |
| 1807 | 1811 | auto iter2 = new2.begin(); |
| 1808 | 1812 | assert(iter2 == new2.end()); |
| 1809 | 1813 | iter2.insertAfter(3, QPDFObjectHandle::newString("3!")); |
| 1810 | - assert((*iter2).first == 3); | |
| 1814 | + assert(iter2->first == 3); | |
| 1811 | 1815 | iter2.insertAfter(4, QPDFObjectHandle::newString("4!")); |
| 1812 | - assert((*iter2).first == 4); | |
| 1813 | - for (auto i: new2) | |
| 1816 | + assert(iter2->first == 4); | |
| 1817 | + for (auto& i: new2) | |
| 1814 | 1818 | { |
| 1815 | 1819 | std::cout << i.first << " " << i.second.unparse() << std::endl; |
| 1816 | 1820 | } |
| ... | ... | @@ -1830,7 +1834,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1830 | 1834 | std::cout << "/Bad2" << std::endl; |
| 1831 | 1835 | auto bad2 = QPDFNumberTreeObjectHelper( |
| 1832 | 1836 | pdf.getTrailer().getKey("/Bad2"), pdf); |
| 1833 | - for (auto i: bad2) | |
| 1837 | + for (auto& i: bad2) | |
| 1834 | 1838 | { |
| 1835 | 1839 | std::cout << i.first << " " << i.second.unparse() << std::endl; |
| 1836 | 1840 | } |
| ... | ... | @@ -1844,21 +1848,21 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1844 | 1848 | assert(empty.begin() == empty.end()); |
| 1845 | 1849 | assert(empty.last() == empty.end()); |
| 1846 | 1850 | auto i = empty.insert(5, QPDFObjectHandle::newString("5")); |
| 1847 | - assert((*i).first == 5); | |
| 1848 | - assert((*i).second.getStringValue() == "5"); | |
| 1849 | - assert((*empty.begin()).first == 5); | |
| 1850 | - assert((*empty.last()).first == 5); | |
| 1851 | - assert((*empty.begin()).second.getStringValue() == "5"); | |
| 1851 | + assert(i->first == 5); | |
| 1852 | + assert(i->second.getStringValue() == "5"); | |
| 1853 | + assert(empty.begin()->first == 5); | |
| 1854 | + assert(empty.last()->first == 5); | |
| 1855 | + assert(empty.begin()->second.getStringValue() == "5"); | |
| 1852 | 1856 | i = empty.insert(5, QPDFObjectHandle::newString("5+")); |
| 1853 | - assert((*i).first == 5); | |
| 1854 | - assert((*i).second.getStringValue() == "5+"); | |
| 1855 | - assert((*empty.begin()).second.getStringValue() == "5+"); | |
| 1857 | + assert(i->first == 5); | |
| 1858 | + assert(i->second.getStringValue() == "5+"); | |
| 1859 | + assert(empty.begin()->second.getStringValue() == "5+"); | |
| 1856 | 1860 | i = empty.insert(6, QPDFObjectHandle::newString("6")); |
| 1857 | - assert((*i).first == 6); | |
| 1858 | - assert((*i).second.getStringValue() == "6"); | |
| 1859 | - assert((*empty.begin()).second.getStringValue() == "5+"); | |
| 1860 | - assert((*empty.last()).first == 6); | |
| 1861 | - assert((*empty.last()).second.getStringValue() == "6"); | |
| 1861 | + assert(i->first == 6); | |
| 1862 | + assert(i->second.getStringValue() == "6"); | |
| 1863 | + assert(empty.begin()->second.getStringValue() == "5+"); | |
| 1864 | + assert(empty.last()->first == 6); | |
| 1865 | + assert(empty.last()->second.getStringValue() == "6"); | |
| 1862 | 1866 | } |
| 1863 | 1867 | std::cout << "Insert into invalid" << std::endl; |
| 1864 | 1868 | auto invalid1 = QPDFNumberTreeObjectHelper( |
| ... | ... | @@ -1875,7 +1879,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1875 | 1879 | std::cout << "/Bad3, no repair" << std::endl; |
| 1876 | 1880 | auto bad3_oh = pdf.getTrailer().getKey("/Bad3"); |
| 1877 | 1881 | auto bad3 = QPDFNumberTreeObjectHelper(bad3_oh, pdf, false); |
| 1878 | - for (auto i: bad3) | |
| 1882 | + for (auto& i: bad3) | |
| 1879 | 1883 | { |
| 1880 | 1884 | std::cout << i.first << " " << i.second.unparse() << std::endl; |
| 1881 | 1885 | } |
| ... | ... | @@ -1883,7 +1887,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1883 | 1887 | |
| 1884 | 1888 | std::cout << "/Bad3, repair" << std::endl; |
| 1885 | 1889 | bad3 = QPDFNumberTreeObjectHelper(bad3_oh, pdf, true); |
| 1886 | - for (auto i: bad3) | |
| 1890 | + for (auto& i: bad3) | |
| 1887 | 1891 | { |
| 1888 | 1892 | std::cout << i.first << " " << i.second.unparse() << std::endl; |
| 1889 | 1893 | } |
| ... | ... | @@ -1893,7 +1897,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1893 | 1897 | auto bad4 = QPDFNumberTreeObjectHelper( |
| 1894 | 1898 | pdf.getTrailer().getKey("/Bad4"), pdf); |
| 1895 | 1899 | bad4.insert(5, QPDFObjectHandle::newString("5")); |
| 1896 | - for (auto i: bad4) | |
| 1900 | + for (auto& i: bad4) | |
| 1897 | 1901 | { |
| 1898 | 1902 | std::cout << i.first << " " << i.second.unparse() << std::endl; |
| 1899 | 1903 | } |
| ... | ... | @@ -1924,7 +1928,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1924 | 1928 | // name-tree.pdf |
| 1925 | 1929 | QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); |
| 1926 | 1930 | QPDFNameTreeObjectHelper ntoh(qtest, pdf); |
| 1927 | - for (auto iter: ntoh) | |
| 1931 | + for (auto& iter: ntoh) | |
| 1928 | 1932 | { |
| 1929 | 1933 | std::cout << iter.first << " -> " |
| 1930 | 1934 | << iter.second.getStringValue() |
| ... | ... | @@ -1945,8 +1949,8 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1945 | 1949 | assert(ntoh.findObject("07 sev\xe2\x80\xa2n", oh)); |
| 1946 | 1950 | assert("seven!" == oh.getStringValue()); |
| 1947 | 1951 | auto last = ntoh.last(); |
| 1948 | - assert((*last).first == "29 twenty-nine"); | |
| 1949 | - assert((*last).second.getUTF8Value() == "twenty-nine!"); | |
| 1952 | + assert(last->first == "29 twenty-nine"); | |
| 1953 | + assert(last->second.getUTF8Value() == "twenty-nine!"); | |
| 1950 | 1954 | |
| 1951 | 1955 | auto new1 = QPDFNameTreeObjectHelper::newEmpty(pdf); |
| 1952 | 1956 | auto iter1 = new1.begin(); |
| ... | ... | @@ -1957,32 +1961,36 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1957 | 1961 | assert(iter1 == new1.end()); |
| 1958 | 1962 | new1.insert("1", QPDFObjectHandle::newString("1")); |
| 1959 | 1963 | ++iter1; |
| 1960 | - assert((*iter1).first == "1"); | |
| 1964 | + assert(iter1->first == "1"); | |
| 1965 | + auto& iter1_val = *iter1; | |
| 1961 | 1966 | --iter1; |
| 1962 | 1967 | assert(iter1 == new1.end()); |
| 1963 | 1968 | --iter1; |
| 1964 | - assert((*iter1).first == "1"); | |
| 1969 | + assert(iter1->first == "1"); | |
| 1970 | + assert(iter1_val.first == "1"); | |
| 1965 | 1971 | new1.insert("2", QPDFObjectHandle::newString("2")); |
| 1966 | 1972 | ++iter1; |
| 1967 | - assert((*iter1).first == "2"); | |
| 1973 | + assert(iter1->first == "2"); | |
| 1974 | + assert(iter1_val.first == "2"); | |
| 1968 | 1975 | ++iter1; |
| 1969 | 1976 | assert(iter1 == new1.end()); |
| 1977 | + assert(! iter1_val.second.isInitialized()); | |
| 1970 | 1978 | ++iter1; |
| 1971 | - assert((*iter1).first == "1"); | |
| 1979 | + assert(iter1->first == "1"); | |
| 1972 | 1980 | --iter1; |
| 1973 | 1981 | assert(iter1 == new1.end()); |
| 1974 | 1982 | --iter1; |
| 1975 | - assert((*iter1).first == "2"); | |
| 1983 | + assert(iter1->first == "2"); | |
| 1976 | 1984 | |
| 1977 | 1985 | std::cout << "insertAfter" << std::endl; |
| 1978 | 1986 | auto new2 = QPDFNameTreeObjectHelper::newEmpty(pdf); |
| 1979 | 1987 | auto iter2 = new2.begin(); |
| 1980 | 1988 | assert(iter2 == new2.end()); |
| 1981 | 1989 | iter2.insertAfter("3", QPDFObjectHandle::newString("3!")); |
| 1982 | - assert((*iter2).first == "3"); | |
| 1990 | + assert(iter2->first == "3"); | |
| 1983 | 1991 | iter2.insertAfter("4", QPDFObjectHandle::newString("4!")); |
| 1984 | - assert((*iter2).first == "4"); | |
| 1985 | - for (auto i: new2) | |
| 1992 | + assert(iter2->first == "4"); | |
| 1993 | + for (auto& i: new2) | |
| 1986 | 1994 | { |
| 1987 | 1995 | std::cout << i.first << " " << i.second.unparse() << std::endl; |
| 1988 | 1996 | } |
| ... | ... | @@ -1996,21 +2004,21 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 1996 | 2004 | assert(empty.begin() == empty.end()); |
| 1997 | 2005 | assert(empty.last() == empty.end()); |
| 1998 | 2006 | auto i = empty.insert("five", QPDFObjectHandle::newString("5")); |
| 1999 | - assert((*i).first == "five"); | |
| 2000 | - assert((*i).second.getStringValue() == "5"); | |
| 2001 | - assert((*empty.begin()).first == "five"); | |
| 2002 | - assert((*empty.last()).first == "five"); | |
| 2003 | - assert((*empty.begin()).second.getStringValue() == "5"); | |
| 2007 | + assert(i->first == "five"); | |
| 2008 | + assert(i->second.getStringValue() == "5"); | |
| 2009 | + assert(empty.begin()->first == "five"); | |
| 2010 | + assert(empty.last()->first == "five"); | |
| 2011 | + assert(empty.begin()->second.getStringValue() == "5"); | |
| 2004 | 2012 | i = empty.insert("five", QPDFObjectHandle::newString("5+")); |
| 2005 | - assert((*i).first == "five"); | |
| 2006 | - assert((*i).second.getStringValue() == "5+"); | |
| 2007 | - assert((*empty.begin()).second.getStringValue() == "5+"); | |
| 2013 | + assert(i->first == "five"); | |
| 2014 | + assert(i->second.getStringValue() == "5+"); | |
| 2015 | + assert(empty.begin()->second.getStringValue() == "5+"); | |
| 2008 | 2016 | i = empty.insert("six", QPDFObjectHandle::newString("6")); |
| 2009 | - assert((*i).first == "six"); | |
| 2010 | - assert((*i).second.getStringValue() == "6"); | |
| 2011 | - assert((*empty.begin()).second.getStringValue() == "5+"); | |
| 2012 | - assert((*empty.last()).first == "six"); | |
| 2013 | - assert((*empty.last()).second.getStringValue() == "6"); | |
| 2017 | + assert(i->first == "six"); | |
| 2018 | + assert(i->second.getStringValue() == "6"); | |
| 2019 | + assert(empty.begin()->second.getStringValue() == "5+"); | |
| 2020 | + assert(empty.last()->first == "six"); | |
| 2021 | + assert(empty.last()->second.getStringValue() == "6"); | |
| 2014 | 2022 | } |
| 2015 | 2023 | |
| 2016 | 2024 | // Exercise deprecated API until qpdf 11 |
| ... | ... | @@ -2030,8 +2038,8 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2030 | 2038 | std::cout << "/Bad1 -- wrong key type" << std::endl; |
| 2031 | 2039 | bad1 = QPDFNameTreeObjectHelper( |
| 2032 | 2040 | pdf.getTrailer().getKey("/Bad1"), pdf); |
| 2033 | - assert((*bad1.find("G", true)).first == "A"); | |
| 2034 | - for (auto i: bad1) | |
| 2041 | + assert(bad1.find("G", true)->first == "A"); | |
| 2042 | + for (auto const& i: bad1) | |
| 2035 | 2043 | { |
| 2036 | 2044 | std::cout << i.first << std::endl; |
| 2037 | 2045 | } |
| ... | ... | @@ -2039,8 +2047,8 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2039 | 2047 | std::cout << "/Bad2 -- invalid kid" << std::endl; |
| 2040 | 2048 | auto bad2 = QPDFNameTreeObjectHelper( |
| 2041 | 2049 | pdf.getTrailer().getKey("/Bad2"), pdf); |
| 2042 | - assert((*bad2.find("G", true)).first == "B"); | |
| 2043 | - for (auto i: bad2) | |
| 2050 | + assert(bad2.find("G", true)->first == "B"); | |
| 2051 | + for (auto const& i: bad2) | |
| 2044 | 2052 | { |
| 2045 | 2053 | std::cout << i.first << std::endl; |
| 2046 | 2054 | } |
| ... | ... | @@ -2053,8 +2061,8 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2053 | 2061 | std::cout << "/Bad4 -- invalid kid" << std::endl; |
| 2054 | 2062 | auto bad4 = QPDFNameTreeObjectHelper( |
| 2055 | 2063 | pdf.getTrailer().getKey("/Bad4"), pdf); |
| 2056 | - assert((*bad4.find("F", true)).first == "C"); | |
| 2057 | - for (auto i: bad4) | |
| 2064 | + assert(bad4.find("F", true)->first == "C"); | |
| 2065 | + for (auto const& i: bad4) | |
| 2058 | 2066 | { |
| 2059 | 2067 | std::cout << i.first << std::endl; |
| 2060 | 2068 | } |
| ... | ... | @@ -2062,12 +2070,12 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2062 | 2070 | std::cout << "/Bad5 -- loop in find" << std::endl; |
| 2063 | 2071 | auto bad5 = QPDFNameTreeObjectHelper( |
| 2064 | 2072 | pdf.getTrailer().getKey("/Bad5"), pdf); |
| 2065 | - assert((*bad5.find("F", true)).first == "D"); | |
| 2073 | + assert(bad5.find("F", true)->first == "D"); | |
| 2066 | 2074 | |
| 2067 | 2075 | std::cout << "/Bad6 -- bad limits" << std::endl; |
| 2068 | 2076 | auto bad6 = QPDFNameTreeObjectHelper( |
| 2069 | 2077 | pdf.getTrailer().getKey("/Bad6"), pdf); |
| 2070 | - assert((*bad6.insert("H", QPDFObjectHandle::newNull())).first == "H"); | |
| 2078 | + assert(bad6.insert("H", QPDFObjectHandle::newNull())->first == "H"); | |
| 2071 | 2079 | } |
| 2072 | 2080 | else if (n == 49) |
| 2073 | 2081 | { |
| ... | ... | @@ -2574,12 +2582,12 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2574 | 2582 | auto check_split1 = [&split1](int k) { |
| 2575 | 2583 | auto i = split1.insert(k, QPDFObjectHandle::newString( |
| 2576 | 2584 | QUtil::int_to_string(k))); |
| 2577 | - assert((*i).first == k); | |
| 2585 | + assert(i->first == k); | |
| 2578 | 2586 | }; |
| 2579 | 2587 | check_split1(15); |
| 2580 | 2588 | check_split1(35); |
| 2581 | 2589 | check_split1(125); |
| 2582 | - for (auto i: split1) | |
| 2590 | + for (auto const& i: split1) | |
| 2583 | 2591 | { |
| 2584 | 2592 | std::cout << i.first << std::endl; |
| 2585 | 2593 | } |
| ... | ... | @@ -2591,10 +2599,10 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2591 | 2599 | auto check_split2 = [](QPDFNameTreeObjectHelper& noh, |
| 2592 | 2600 | std::string const& k) { |
| 2593 | 2601 | auto i = noh.insert(k, QPDFObjectHandle::newUnicodeString(k)); |
| 2594 | - assert((*i).first == k); | |
| 2602 | + assert(i->first == k); | |
| 2595 | 2603 | }; |
| 2596 | 2604 | check_split2(split2, "C"); |
| 2597 | - for (auto i: split2) | |
| 2605 | + for (auto const& i: split2) | |
| 2598 | 2606 | { |
| 2599 | 2607 | std::cout << i.first << std::endl; |
| 2600 | 2608 | } |
| ... | ... | @@ -2605,7 +2613,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2605 | 2613 | split3.setSplitThreshold(4); |
| 2606 | 2614 | check_split2(split3, "P"); |
| 2607 | 2615 | check_split2(split3, "\xcf\x80"); |
| 2608 | - for (auto i: split3) | |
| 2616 | + for (auto& i: split3) | |
| 2609 | 2617 | { |
| 2610 | 2618 | std::cout << i.first << " " << i.second.unparse() << std::endl; |
| 2611 | 2619 | } |
| ... | ... | @@ -2626,11 +2634,11 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2626 | 2634 | assert(value.getUTF8Value() == "c"); |
| 2627 | 2635 | auto iter1 = erase1.find("1B"); |
| 2628 | 2636 | iter1.remove(); |
| 2629 | - assert((*iter1).first == "1D"); | |
| 2637 | + assert(iter1->first == "1D"); | |
| 2630 | 2638 | iter1.remove(); |
| 2631 | 2639 | assert(iter1 == erase1.end()); |
| 2632 | 2640 | --iter1; |
| 2633 | - assert((*iter1).first == "1A"); | |
| 2641 | + assert(iter1->first == "1A"); | |
| 2634 | 2642 | iter1.remove(); |
| 2635 | 2643 | assert(iter1 == erase1.end()); |
| 2636 | 2644 | |
| ... | ... | @@ -2640,14 +2648,14 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2640 | 2648 | iter2.remove(); |
| 2641 | 2649 | assert(iter2 == erase2.end()); |
| 2642 | 2650 | --iter2; |
| 2643 | - assert((*iter2).first == 240); | |
| 2651 | + assert(iter2->first == 240); | |
| 2644 | 2652 | auto k1 = erase2_oh.getKey("/Kids").getArrayItem(1); |
| 2645 | 2653 | auto l1 = k1.getKey("/Limits"); |
| 2646 | 2654 | assert(l1.getArrayItem(0).getIntValue() == 230); |
| 2647 | 2655 | assert(l1.getArrayItem(1).getIntValue() == 240); |
| 2648 | 2656 | iter2 = erase2.find(210); |
| 2649 | 2657 | iter2.remove(); |
| 2650 | - assert((*iter2).first == 220); | |
| 2658 | + assert(iter2->first == 220); | |
| 2651 | 2659 | k1 = erase2_oh.getKey("/Kids").getArrayItem(0); |
| 2652 | 2660 | l1 = k1.getKey("/Limits"); |
| 2653 | 2661 | assert(l1.getArrayItem(0).getIntValue() == 220); |
| ... | ... | @@ -2667,7 +2675,7 @@ void runtest(int n, char const* filename1, char const* arg2) |
| 2667 | 2675 | pdf.getTrailer().getKey("/Erase4"), pdf); |
| 2668 | 2676 | iter2 = erase4.find(420); |
| 2669 | 2677 | iter2.remove(); |
| 2670 | - assert((*iter2).first == 430); | |
| 2678 | + assert(iter2->first == 430); | |
| 2671 | 2679 | |
| 2672 | 2680 | QPDFWriter w(pdf, "a.pdf"); |
| 2673 | 2681 | w.setStaticID(true); | ... | ... |