Commit 8ed3e8c79b5cbccfeccee865e555b68025ee2c1f

Authored by Jay Berkenbilt
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.
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&amp; 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&amp; 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&amp; 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&amp;
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&amp;
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&amp;
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&amp;
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&lt;
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&lt;
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&lt;
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);
... ...