Commit db288584617600ba09f5ebbd59bf14fee4d6b6c7

Authored by m-holger
Committed by GitHub
2 parents f5083840 1674b67c

Merge pull request #1522 from m-holger/oh_array

Add private-API subscript operators to `QPDFObjectHandle`
include/qpdf/ObjectHandle.hh
@@ -73,6 +73,10 @@ namespace qpdf @@ -73,6 +73,10 @@ namespace qpdf
73 return size() == 0; 73 return size() == 0;
74 } 74 }
75 75
  76 + QPDFObjectHandle operator[](size_t n) const;
  77 +
  78 + QPDFObjectHandle operator[](int n) const;
  79 +
76 std::shared_ptr<QPDFObject> copy(bool shallow = false) const; 80 std::shared_ptr<QPDFObject> copy(bool shallow = false) const;
77 // Recursively remove association with any QPDF object. This method may only be called 81 // Recursively remove association with any QPDF object. This method may only be called
78 // during final destruction. 82 // during final destruction.
libqpdf/NNTree.cc
  1 +#include <qpdf/assert_debug.h>
  2 +
1 #include <qpdf/NNTree.hh> 3 #include <qpdf/NNTree.hh>
2 4
  5 +#include <qpdf/QPDFObjectHandle_private.hh>
3 #include <qpdf/QPDF_private.hh> 6 #include <qpdf/QPDF_private.hh>
4 #include <qpdf/QTC.hh> 7 #include <qpdf/QTC.hh>
5 #include <qpdf/QUtil.hh> 8 #include <qpdf/QUtil.hh>
6 9
  10 +#include <bit>
7 #include <exception> 11 #include <exception>
  12 +#include <utility>
8 13
9 static std::string 14 static std::string
10 -get_description(QPDFObjectHandle& node) 15 +get_description(QPDFObjectHandle const& node)
11 { 16 {
12 std::string result("Name/Number tree node"); 17 std::string result("Name/Number tree node");
13 - if (node.isIndirect()) { 18 + if (node.indirect()) {
14 result += " (object " + std::to_string(node.getObjectID()) + ")"; 19 result += " (object " + std::to_string(node.getObjectID()) + ")";
15 } 20 }
16 return result; 21 return result;
17 } 22 }
18 23
19 void 24 void
20 -NNTreeImpl::warn(QPDFObjectHandle& node, std::string const& msg) 25 +NNTreeImpl::warn(QPDFObjectHandle const& node, std::string const& msg)
21 { 26 {
22 qpdf.warn(qpdf_e_damaged_pdf, get_description(node), 0, msg); 27 qpdf.warn(qpdf_e_damaged_pdf, get_description(node), 0, msg);
23 if (++error_count > 5 && qpdf.reconstructed_xref()) { 28 if (++error_count > 5 && qpdf.reconstructed_xref()) {
@@ -26,7 +31,7 @@ NNTreeImpl::warn(QPDFObjectHandle&amp; node, std::string const&amp; msg) @@ -26,7 +31,7 @@ NNTreeImpl::warn(QPDFObjectHandle&amp; node, std::string const&amp; msg)
26 } 31 }
27 32
28 void 33 void
29 -NNTreeImpl::error(QPDFObjectHandle& node, std::string const& msg) 34 +NNTreeImpl::error(QPDFObjectHandle const& node, std::string const& msg)
30 { 35 {
31 throw QPDFExc(qpdf_e_damaged_pdf, qpdf.getFilename(), get_description(node), 0, msg); 36 throw QPDFExc(qpdf_e_damaged_pdf, qpdf.getFilename(), get_description(node), 0, msg);
32 } 37 }
@@ -52,25 +57,21 @@ NNTreeIterator::updateIValue(bool allow_invalid) @@ -52,25 +57,21 @@ NNTreeIterator::updateIValue(bool allow_invalid)
52 // we must call updateIValue as well. These cases are handled, and for good measure, we also 57 // we must call updateIValue as well. These cases are handled, and for good measure, we also
53 // call updateIValue in operator* and operator->. 58 // call updateIValue in operator* and operator->.
54 59
55 - bool okay = false;  
56 - if (item_number >= 0 && node.isDictionary()) {  
57 - auto items = node.getKey(impl.details.itemsKey());  
58 - if (item_number + 1 < items.getArrayNItems()) {  
59 - okay = true;  
60 - ivalue.first = items.getArrayItem(item_number);  
61 - ivalue.second = items.getArrayItem(1 + item_number);  
62 - } else {  
63 - impl.error(node, "update ivalue: items array is too short");  
64 - }  
65 - }  
66 - if (!okay) { 60 + if (item_number < 0 || !node.isDictionary()) {
67 if (!allow_invalid) { 61 if (!allow_invalid) {
68 throw std::logic_error( 62 throw std::logic_error(
69 "attempt made to dereference an invalid name/number tree iterator"); 63 "attempt made to dereference an invalid name/number tree iterator");
70 } 64 }
71 ivalue.first = QPDFObjectHandle(); 65 ivalue.first = QPDFObjectHandle();
72 ivalue.second = QPDFObjectHandle(); 66 ivalue.second = QPDFObjectHandle();
  67 + return;
  68 + }
  69 + auto items = node.getKey(impl.details.itemsKey());
  70 + if (!std::cmp_less(item_number + 1, items.size())) {
  71 + impl.error(node, "update ivalue: items array is too short");
73 } 72 }
  73 + ivalue.first = items[item_number];
  74 + ivalue.second = items[1 + item_number];
74 } 75 }
75 76
76 NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const& node, int kid_number) : 77 NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const& node, int kid_number) :
@@ -82,28 +83,22 @@ NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const&amp; node, int kid_n @@ -82,28 +83,22 @@ NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const&amp; node, int kid_n
82 QPDFObjectHandle 83 QPDFObjectHandle
83 NNTreeIterator::getNextKid(PathElement& pe, bool backward) 84 NNTreeIterator::getNextKid(PathElement& pe, bool backward)
84 { 85 {
85 - QPDFObjectHandle result;  
86 - bool found = false;  
87 - while (!found) { 86 + while (true) {
88 pe.kid_number += backward ? -1 : 1; 87 pe.kid_number += backward ? -1 : 1;
89 auto kids = pe.node.getKey("/Kids"); 88 auto kids = pe.node.getKey("/Kids");
90 - if ((pe.kid_number >= 0) && (pe.kid_number < kids.getArrayNItems())) {  
91 - result = kids.getArrayItem(pe.kid_number); 89 + if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) {
  90 + auto result = kids[pe.kid_number];
92 if (result.isDictionary() && 91 if (result.isDictionary() &&
93 (result.hasKey("/Kids") || result.hasKey(impl.details.itemsKey()))) { 92 (result.hasKey("/Kids") || result.hasKey(impl.details.itemsKey()))) {
94 - found = true; 93 + return result;
95 } else { 94 } else {
96 - QTC::TC("qpdf", "NNTree skip invalid kid");  
97 impl.warn( 95 impl.warn(
98 - pe.node,  
99 - ("skipping over invalid kid at index " + std::to_string(pe.kid_number))); 96 + pe.node, "skipping over invalid kid at index " + std::to_string(pe.kid_number));
100 } 97 }
101 } else { 98 } else {
102 - result = QPDFObjectHandle::newNull();  
103 - found = true; 99 + return QPDFObjectHandle::newNull();
104 } 100 }
105 } 101 }
106 - return result;  
107 } 102 }
108 103
109 bool 104 bool
@@ -116,21 +111,20 @@ void @@ -116,21 +111,20 @@ void
116 NNTreeIterator::increment(bool backward) 111 NNTreeIterator::increment(bool backward)
117 { 112 {
118 if (item_number < 0) { 113 if (item_number < 0) {
119 - QTC::TC("qpdf", "NNTree increment end()");  
120 deepen(impl.oh, !backward, true); 114 deepen(impl.oh, !backward, true);
121 return; 115 return;
122 } 116 }
123 - bool found_valid_key = false;  
124 - while (valid() && !found_valid_key) { 117 +
  118 + while (valid()) {
125 item_number += backward ? -2 : 2; 119 item_number += backward ? -2 : 2;
126 auto items = node.getKey(impl.details.itemsKey()); 120 auto items = node.getKey(impl.details.itemsKey());
127 - if (item_number < 0 || item_number >= items.getArrayNItems()) { 121 + if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) {
128 bool found = false; 122 bool found = false;
129 setItemNumber(QPDFObjectHandle(), -1); 123 setItemNumber(QPDFObjectHandle(), -1);
130 while (!(found || path.empty())) { 124 while (!(found || path.empty())) {
131 auto& element = path.back(); 125 auto& element = path.back();
132 auto pe_node = getNextKid(element, backward); 126 auto pe_node = getNextKid(element, backward);
133 - if (pe_node.isNull()) { 127 + if (pe_node.null()) {
134 path.pop_back(); 128 path.pop_back();
135 } else { 129 } else {
136 found = deepen(pe_node, !backward, false); 130 found = deepen(pe_node, !backward, false);
@@ -139,14 +133,12 @@ NNTreeIterator::increment(bool backward) @@ -139,14 +133,12 @@ NNTreeIterator::increment(bool backward)
139 } 133 }
140 if (item_number >= 0) { 134 if (item_number >= 0) {
141 items = node.getKey(impl.details.itemsKey()); 135 items = node.getKey(impl.details.itemsKey());
142 - if (item_number + 1 >= items.getArrayNItems()) {  
143 - QTC::TC("qpdf", "NNTree skip item at end of short items"); 136 + if (std::cmp_greater_equal(item_number + 1, items.size())) {
144 impl.warn(node, "items array doesn't have enough elements"); 137 impl.warn(node, "items array doesn't have enough elements");
145 - } else if (!impl.details.keyValid(items.getArrayItem(item_number))) {  
146 - QTC::TC("qpdf", "NNTree skip invalid key"); 138 + } else if (!impl.details.keyValid(items[item_number])) {
147 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type")); 139 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type"));
148 } else { 140 } else {
149 - found_valid_key = true; 141 + return;
150 } 142 }
151 } 143 }
152 } 144 }
@@ -155,35 +147,31 @@ NNTreeIterator::increment(bool backward) @@ -155,35 +147,31 @@ NNTreeIterator::increment(bool backward)
155 void 147 void
156 NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list<PathElement>::iterator parent) 148 NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list<PathElement>::iterator parent)
157 { 149 {
158 - bool done = false;  
159 - while (!done) { 150 + while (true) {
160 if (parent == path.end()) { 151 if (parent == path.end()) {
161 - QTC::TC("qpdf", "NNTree remove limits from root");  
162 a_node.removeKey("/Limits"); 152 a_node.removeKey("/Limits");
163 - done = true;  
164 break; 153 break;
165 } 154 }
166 auto kids = a_node.getKey("/Kids"); 155 auto kids = a_node.getKey("/Kids");
167 - int nkids = kids.isArray() ? kids.getArrayNItems() : 0; 156 + size_t nkids = kids.isArray() ? kids.size() : 0;
168 auto items = a_node.getKey(impl.details.itemsKey()); 157 auto items = a_node.getKey(impl.details.itemsKey());
169 - int nitems = items.isArray() ? items.getArrayNItems() : 0; 158 + size_t nitems = items.size();
170 159
171 bool changed = true; 160 bool changed = true;
172 QPDFObjectHandle first; 161 QPDFObjectHandle first;
173 QPDFObjectHandle last; 162 QPDFObjectHandle last;
174 if (nitems >= 2) { 163 if (nitems >= 2) {
175 - first = items.getArrayItem(0);  
176 - last = items.getArrayItem((nitems - 1) & ~1); 164 + first = items[0];
  165 + last = items[(nitems - 1u) & ~1u];
177 } else if (nkids > 0) { 166 } else if (nkids > 0) {
178 - auto first_kid = kids.getArrayItem(0);  
179 - auto last_kid = kids.getArrayItem(nkids - 1); 167 + auto first_kid = kids[0];
  168 + auto last_kid = kids[nkids - 1u];
180 if (first_kid.isDictionary() && last_kid.isDictionary()) { 169 if (first_kid.isDictionary() && last_kid.isDictionary()) {
181 auto first_limits = first_kid.getKey("/Limits"); 170 auto first_limits = first_kid.getKey("/Limits");
182 auto last_limits = last_kid.getKey("/Limits"); 171 auto last_limits = last_kid.getKey("/Limits");
183 - if (first_limits.isArray() && (first_limits.getArrayNItems() >= 2) &&  
184 - last_limits.isArray() && (last_limits.getArrayNItems() >= 2)) {  
185 - first = first_limits.getArrayItem(0);  
186 - last = last_limits.getArrayItem(1); 172 + if (first_limits.size() >= 2 && last_limits.size() >= 2) {
  173 + first = first_limits[0];
  174 + last = last_limits[1];
187 } 175 }
188 } 176 }
189 } 177 }
@@ -192,13 +180,12 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -192,13 +180,12 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
192 limits.appendItem(first); 180 limits.appendItem(first);
193 limits.appendItem(last); 181 limits.appendItem(last);
194 auto olimits = a_node.getKey("/Limits"); 182 auto olimits = a_node.getKey("/Limits");
195 - if (olimits.isArray() && (olimits.getArrayNItems() == 2)) {  
196 - auto ofirst = olimits.getArrayItem(0);  
197 - auto olast = olimits.getArrayItem(1); 183 + if (olimits.size() == 2) {
  184 + auto ofirst = olimits[0];
  185 + auto olast = olimits[1];
198 if (impl.details.keyValid(ofirst) && impl.details.keyValid(olast) && 186 if (impl.details.keyValid(ofirst) && impl.details.keyValid(olast) &&
199 (impl.details.compareKeys(first, ofirst) == 0) && 187 (impl.details.compareKeys(first, ofirst) == 0) &&
200 (impl.details.compareKeys(last, olast) == 0)) { 188 (impl.details.compareKeys(last, olast) == 0)) {
201 - QTC::TC("qpdf", "NNTree limits didn't change");  
202 changed = false; 189 changed = false;
203 } 190 }
204 } 191 }
@@ -206,12 +193,11 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -206,12 +193,11 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
206 a_node.replaceKey("/Limits", limits); 193 a_node.replaceKey("/Limits", limits);
207 } 194 }
208 } else { 195 } else {
209 - QTC::TC("qpdf", "NNTree unable to determine limits");  
210 impl.warn(a_node, "unable to determine limits"); 196 impl.warn(a_node, "unable to determine limits");
211 } 197 }
212 198
213 if (!changed || parent == path.begin()) { 199 if (!changed || parent == path.begin()) {
214 - done = true; 200 + break;
215 } else { 201 } else {
216 a_node = parent->node; 202 a_node = parent->node;
217 --parent; 203 --parent;
@@ -255,22 +241,20 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -255,22 +241,20 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
255 241
256 // Find the array we actually need to split, which is either this node's kids or items. 242 // Find the array we actually need to split, which is either this node's kids or items.
257 auto kids = to_split.getKey("/Kids"); 243 auto kids = to_split.getKey("/Kids");
258 - int nkids = kids.isArray() ? kids.getArrayNItems() : 0; 244 + int nkids = kids.isArray() ? static_cast<int>(kids.size()) : 0;
259 auto items = to_split.getKey(impl.details.itemsKey()); 245 auto items = to_split.getKey(impl.details.itemsKey());
260 - int nitems = items.isArray() ? items.getArrayNItems() : 0; 246 + int nitems = items.isArray() ? static_cast<int>(items.size()) : 0;
261 247
262 QPDFObjectHandle first_half; 248 QPDFObjectHandle first_half;
263 int n = 0; 249 int n = 0;
264 std::string key; 250 std::string key;
265 int threshold = 0; 251 int threshold = 0;
266 if (nkids > 0) { 252 if (nkids > 0) {
267 - QTC::TC("qpdf", "NNTree split kids");  
268 first_half = kids; 253 first_half = kids;
269 n = nkids; 254 n = nkids;
270 threshold = impl.split_threshold; 255 threshold = impl.split_threshold;
271 key = "/Kids"; 256 key = "/Kids";
272 } else if (nitems > 0) { 257 } else if (nitems > 0) {
273 - QTC::TC("qpdf", "NNTree split items");  
274 first_half = items; 258 first_half = items;
275 n = nitems; 259 n = nitems;
276 threshold = 2 * impl.split_threshold; 260 threshold = 2 * impl.split_threshold;
@@ -283,8 +267,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -283,8 +267,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
283 return; 267 return;
284 } 268 }
285 269
286 - bool is_root = (parent == path.end());  
287 - bool is_leaf = (nitems > 0); 270 + bool is_root = parent == path.end();
  271 + bool is_leaf = nitems > 0;
288 272
289 // CURRENT STATE: tree is in original state; iterator is valid and unchanged. 273 // CURRENT STATE: tree is in original state; iterator is valid and unchanged.
290 274
@@ -311,10 +295,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -311,10 +295,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
311 to_split.removeKey(impl.details.itemsKey()); 295 to_split.removeKey(impl.details.itemsKey());
312 to_split.replaceKey("/Kids", new_kids); 296 to_split.replaceKey("/Kids", new_kids);
313 if (is_leaf) { 297 if (is_leaf) {
314 - QTC::TC("qpdf", "NNTree split root + leaf");  
315 node = first_node; 298 node = first_node;
316 } else { 299 } else {
317 - QTC::TC("qpdf", "NNTree split root + !leaf");  
318 auto next = path.begin(); 300 auto next = path.begin();
319 next->node = first_node; 301 next->node = first_node;
320 } 302 }
@@ -330,8 +312,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -330,8 +312,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
330 // array. 312 // array.
331 QPDFObjectHandle second_half = QPDFObjectHandle::newArray(); 313 QPDFObjectHandle second_half = QPDFObjectHandle::newArray();
332 int start_idx = ((n / 2) & ~1); 314 int start_idx = ((n / 2) & ~1);
333 - while (first_half.getArrayNItems() > start_idx) {  
334 - second_half.appendItem(first_half.getArrayItem(start_idx)); 315 + while (std::cmp_greater(first_half.size(), start_idx)) {
  316 + second_half.appendItem(first_half[start_idx]);
335 first_half.eraseItem(start_idx); 317 first_half.eraseItem(start_idx);
336 } 318 }
337 resetLimits(to_split, parent); 319 resetLimits(to_split, parent);
@@ -357,16 +339,13 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -357,16 +339,13 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
357 if (old_idx >= start_idx) { 339 if (old_idx >= start_idx) {
358 ++parent->kid_number; 340 ++parent->kid_number;
359 if (is_leaf) { 341 if (is_leaf) {
360 - QTC::TC("qpdf", "NNTree split second half item");  
361 setItemNumber(second_node, item_number - start_idx); 342 setItemNumber(second_node, item_number - start_idx);
362 } else { 343 } else {
363 - QTC::TC("qpdf", "NNTree split second half kid");  
364 cur_elem->node = second_node; 344 cur_elem->node = second_node;
365 cur_elem->kid_number -= start_idx; 345 cur_elem->kid_number -= start_idx;
366 } 346 }
367 } 347 }
368 if (!is_root) { 348 if (!is_root) {
369 - QTC::TC("qpdf", "NNTree split parent");  
370 auto next = parent->node; 349 auto next = parent->node;
371 resetLimits(next, parent); 350 resetLimits(next, parent);
372 --parent; 351 --parent;
@@ -385,7 +364,7 @@ NNTreeIterator::lastPathElement() @@ -385,7 +364,7 @@ NNTreeIterator::lastPathElement()
385 } 364 }
386 365
387 void 366 void
388 -NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) 367 +NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& value)
389 { 368 {
390 if (!valid()) { 369 if (!valid()) {
391 QTC::TC("qpdf", "NNTree insertAfter inserts first"); 370 QTC::TC("qpdf", "NNTree insertAfter inserts first");
@@ -395,10 +374,11 @@ NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) @@ -395,10 +374,11 @@ NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value)
395 } 374 }
396 375
397 auto items = node.getKey(impl.details.itemsKey()); 376 auto items = node.getKey(impl.details.itemsKey());
398 - if (!items.isArray()) {  
399 - impl.error(node, "node contains no items array");  
400 - }  
401 - if (items.getArrayNItems() < item_number + 2) { 377 +
  378 + if (std::cmp_less(items.size(), item_number + 2)) {
  379 + if (!items.isArray()) {
  380 + impl.error(node, "node contains no items array");
  381 + }
402 impl.error(node, "insert: items array is too short"); 382 impl.error(node, "insert: items array is too short");
403 } 383 }
404 items.insertItem(item_number + 2, key); 384 items.insertItem(item_number + 2, key);
@@ -417,8 +397,8 @@ NNTreeIterator::remove() @@ -417,8 +397,8 @@ NNTreeIterator::remove()
417 throw std::logic_error("attempt made to remove an invalid iterator"); 397 throw std::logic_error("attempt made to remove an invalid iterator");
418 } 398 }
419 auto items = node.getKey(impl.details.itemsKey()); 399 auto items = node.getKey(impl.details.itemsKey());
420 - int nitems = items.getArrayNItems();  
421 - if (item_number + 2 > nitems) { 400 + int nitems = static_cast<int>(items.size());
  401 + if (std::cmp_greater(item_number + 2, nitems)) {
422 impl.error(node, "found short items array while removing an item"); 402 impl.error(node, "found short items array while removing an item");
423 } 403 }
424 404
@@ -432,20 +412,17 @@ NNTreeIterator::remove() @@ -432,20 +412,17 @@ NNTreeIterator::remove()
432 if (item_number == 0 || item_number == nitems) { 412 if (item_number == 0 || item_number == nitems) {
433 // We removed either the first or last item of an items array that remains non-empty, so 413 // We removed either the first or last item of an items array that remains non-empty, so
434 // we have to adjust limits. 414 // we have to adjust limits.
435 - QTC::TC("qpdf", "NNTree remove reset limits");  
436 resetLimits(node, lastPathElement()); 415 resetLimits(node, lastPathElement());
437 } 416 }
438 417
439 if (item_number == nitems) { 418 if (item_number == nitems) {
440 // We removed the last item of a non-empty items array, so advance to the successor of 419 // We removed the last item of a non-empty items array, so advance to the successor of
441 // the previous item. 420 // the previous item.
442 - QTC::TC("qpdf", "NNTree erased last item");  
443 item_number -= 2; 421 item_number -= 2;
444 increment(false); 422 increment(false);
445 } else if (item_number < nitems) { 423 } else if (item_number < nitems) {
446 // We don't have to do anything since the removed item's successor now occupies its 424 // We don't have to do anything since the removed item's successor now occupies its
447 // former location. 425 // former location.
448 - QTC::TC("qpdf", "NNTree erased non-last item");  
449 updateIValue(); 426 updateIValue();
450 } else { 427 } else {
451 // We already checked to ensure this condition would not happen. 428 // We already checked to ensure this condition would not happen.
@@ -456,61 +433,51 @@ NNTreeIterator::remove() @@ -456,61 +433,51 @@ NNTreeIterator::remove()
456 433
457 if (path.empty()) { 434 if (path.empty()) {
458 // Special case: if this is the root node, we can leave it empty. 435 // Special case: if this is the root node, we can leave it empty.
459 - QTC::TC("qpdf", "NNTree erased all items on leaf/root");  
460 setItemNumber(impl.oh, -1); 436 setItemNumber(impl.oh, -1);
461 return; 437 return;
462 } 438 }
463 439
464 - QTC::TC("qpdf", "NNTree items is empty after remove");  
465 -  
466 // We removed the last item from this items array, so we need to remove this node from the 440 // We removed the last item from this items array, so we need to remove this node from the
467 // parent on up the tree. Then we need to position ourselves at the removed item's successor. 441 // parent on up the tree. Then we need to position ourselves at the removed item's successor.
468 - bool done = false;  
469 - while (!done) { 442 + while (true) {
470 auto element = lastPathElement(); 443 auto element = lastPathElement();
471 auto parent = element; 444 auto parent = element;
472 --parent; 445 --parent;
473 auto kids = element->node.getKey("/Kids"); 446 auto kids = element->node.getKey("/Kids");
474 kids.eraseItem(element->kid_number); 447 kids.eraseItem(element->kid_number);
475 - auto nkids = kids.getArrayNItems(); 448 + auto nkids = kids.size();
476 if (nkids > 0) { 449 if (nkids > 0) {
477 // The logic here is similar to the items case. 450 // The logic here is similar to the items case.
478 - if ((element->kid_number == 0) || (element->kid_number == nkids)) {  
479 - QTC::TC("qpdf", "NNTree erased first or last kid"); 451 + if (element->kid_number == 0 || std::cmp_equal(element->kid_number, nkids)) {
480 resetLimits(element->node, parent); 452 resetLimits(element->node, parent);
481 } 453 }
482 - if (element->kid_number == nkids) { 454 + if (std::cmp_equal(element->kid_number, nkids)) {
483 // Move to the successor of the last child of the previous kid. 455 // Move to the successor of the last child of the previous kid.
484 - setItemNumber(QPDFObjectHandle(), -1); 456 + setItemNumber({}, -1);
485 --element->kid_number; 457 --element->kid_number;
486 - deepen(kids.getArrayItem(element->kid_number), false, true); 458 + deepen(kids[element->kid_number], false, true);
487 if (valid()) { 459 if (valid()) {
488 increment(false); 460 increment(false);
489 - if (!valid()) {  
490 - QTC::TC("qpdf", "NNTree erased last item in tree");  
491 - } else {  
492 - QTC::TC("qpdf", "NNTree erased last kid");  
493 - } 461 + QTC::TC("qpdf", "NNTree erased last kid/item in tree", valid() ? 0 : 1);
494 } 462 }
495 } else { 463 } else {
496 // Next kid is in deleted kid's position 464 // Next kid is in deleted kid's position
497 - QTC::TC("qpdf", "NNTree erased non-last kid");  
498 deepen(kids.getArrayItem(element->kid_number), true, true); 465 deepen(kids.getArrayItem(element->kid_number), true, true);
499 } 466 }
500 - done = true;  
501 - } else if (parent == path.end()) { 467 + return;
  468 + }
  469 +
  470 + if (parent == path.end()) {
502 // We erased the very last item. Convert the root to an empty items array. 471 // We erased the very last item. Convert the root to an empty items array.
503 - QTC::TC("qpdf", "NNTree non-flat tree is empty after remove");  
504 element->node.removeKey("/Kids"); 472 element->node.removeKey("/Kids");
505 element->node.replaceKey(impl.details.itemsKey(), QPDFObjectHandle::newArray()); 473 element->node.replaceKey(impl.details.itemsKey(), QPDFObjectHandle::newArray());
506 path.clear(); 474 path.clear();
507 setItemNumber(impl.oh, -1); 475 setItemNumber(impl.oh, -1);
508 - done = true;  
509 - } else {  
510 - // Walk up the tree and continue  
511 - QTC::TC("qpdf", "NNTree remove walking up tree");  
512 - path.pop_back(); 476 + return;
513 } 477 }
  478 +
  479 + // Walk up the tree and continue
  480 + path.pop_back();
514 } 481 }
515 } 482 }
516 483
@@ -587,74 +554,64 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -587,74 +554,64 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
587 // items. If we succeed, return true; otherwise return false and leave path alone. 554 // items. If we succeed, return true; otherwise return false and leave path alone.
588 555
589 auto opath = path; 556 auto opath = path;
590 - bool failed = false; 557 +
  558 + auto fail = [this, &opath](QPDFObjectHandle const& failed_node, std::string const& msg) {
  559 + impl.warn(failed_node, msg);
  560 + path = opath;
  561 + return false;
  562 + };
591 563
592 QPDFObjGen::set seen; 564 QPDFObjGen::set seen;
593 for (auto const& i: path) { 565 for (auto const& i: path) {
594 seen.add(i.node); 566 seen.add(i.node);
595 } 567 }
596 - while (!failed) { 568 + while (true) {
597 if (!seen.add(a_node)) { 569 if (!seen.add(a_node)) {
598 - QTC::TC("qpdf", "NNTree deepen: loop");  
599 - impl.warn(a_node, "loop detected while traversing name/number tree");  
600 - failed = true;  
601 - break; 570 + return fail(a_node, "loop detected while traversing name/number tree");
602 } 571 }
603 572
604 if (!a_node.isDictionary()) { 573 if (!a_node.isDictionary()) {
605 - QTC::TC("qpdf", "NNTree node is not a dictionary");  
606 - impl.warn(a_node, "non-dictionary node while traversing name/number tree");  
607 - failed = true;  
608 - break; 574 + return fail(a_node, "non-dictionary node while traversing name/number tree");
609 } 575 }
610 576
611 - auto kids = a_node.getKey("/Kids");  
612 - int nkids = kids.isArray() ? kids.getArrayNItems() : 0;  
613 auto items = a_node.getKey(impl.details.itemsKey()); 577 auto items = a_node.getKey(impl.details.itemsKey());
614 - int nitems = items.isArray() ? items.getArrayNItems() : 0;  
615 - if (nitems > 0) { 578 + int nitems = static_cast<int>(items.size());
  579 + if (nitems > 1) {
616 setItemNumber(a_node, first ? 0 : nitems - 2); 580 setItemNumber(a_node, first ? 0 : nitems - 2);
617 break; 581 break;
618 - } else if (nkids > 0) { 582 + }
  583 +
  584 + auto kids = a_node.getKey("/Kids");
  585 + int nkids = kids.isArray() ? static_cast<int>(kids.size()) : 0;
  586 + if (nkids > 0) {
619 int kid_number = first ? 0 : nkids - 1; 587 int kid_number = first ? 0 : nkids - 1;
620 addPathElement(a_node, kid_number); 588 addPathElement(a_node, kid_number);
621 - auto next = kids.getArrayItem(kid_number); 589 + auto next = kids[kid_number];
622 if (!next.isIndirect()) { 590 if (!next.isIndirect()) {
623 if (impl.auto_repair) { 591 if (impl.auto_repair) {
624 - QTC::TC("qpdf", "NNTree fix indirect kid");  
625 impl.warn( 592 impl.warn(
626 a_node, 593 a_node,
627 - ("converting kid number " + std::to_string(kid_number) +  
628 - " to an indirect object")); 594 + "converting kid number " + std::to_string(kid_number) +
  595 + " to an indirect object");
629 next = impl.qpdf.makeIndirectObject(next); 596 next = impl.qpdf.makeIndirectObject(next);
630 kids.setArrayItem(kid_number, next); 597 kids.setArrayItem(kid_number, next);
631 } else { 598 } else {
632 - QTC::TC("qpdf", "NNTree warn indirect kid");  
633 impl.warn( 599 impl.warn(
634 a_node, 600 a_node,
635 - ("kid number " + std::to_string(kid_number) +  
636 - " is not an indirect object")); 601 + "kid number " + std::to_string(kid_number) + " is not an indirect object");
637 } 602 }
638 } 603 }
639 a_node = next; 604 a_node = next;
640 } else if (allow_empty && items.isArray()) { 605 } else if (allow_empty && items.isArray()) {
641 - QTC::TC("qpdf", "NNTree deepen found empty");  
642 setItemNumber(a_node, -1); 606 setItemNumber(a_node, -1);
643 break; 607 break;
644 } else { 608 } else {
645 - QTC::TC("qpdf", "NNTree deepen: invalid node");  
646 - impl.warn( 609 + return fail(
647 a_node, 610 a_node,
648 - ("name/number tree node has neither non-empty " + impl.details.itemsKey() +  
649 - " nor /Kids"));  
650 - failed = true;  
651 - break; 611 + "name/number tree node has neither non-empty " + impl.details.itemsKey() +
  612 + " nor /Kids");
652 } 613 }
653 } 614 }
654 - if (failed) {  
655 - path = opath;  
656 - return false;  
657 - }  
658 return true; 615 return true;
659 } 616 }
660 617
@@ -696,103 +653,81 @@ NNTreeImpl::last() @@ -696,103 +653,81 @@ NNTreeImpl::last()
696 } 653 }
697 654
698 int 655 int
699 -NNTreeImpl::withinLimits(QPDFObjectHandle key, QPDFObjectHandle node) 656 +NNTreeImpl::withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node)
700 { 657 {
701 - int result = 0;  
702 auto limits = node.getKey("/Limits"); 658 auto limits = node.getKey("/Limits");
703 - if (limits.isArray() && (limits.getArrayNItems() >= 2) &&  
704 - details.keyValid(limits.getArrayItem(0)) && details.keyValid(limits.getArrayItem(1))) {  
705 - if (details.compareKeys(key, limits.getArrayItem(0)) < 0) {  
706 - result = -1;  
707 - } else if (details.compareKeys(key, limits.getArrayItem(1)) > 0) {  
708 - result = 1;  
709 - }  
710 - } else {  
711 - QTC::TC("qpdf", "NNTree missing limits"); 659 + if (!(limits.size() >= 2 && details.keyValid(limits[0]) && details.keyValid(limits[1]))) {
712 error(node, "node is missing /Limits"); 660 error(node, "node is missing /Limits");
713 } 661 }
714 - return result; 662 + if (details.compareKeys(key, limits[0]) < 0) {
  663 + return -1;
  664 + }
  665 + if (details.compareKeys(key, limits[1]) > 0) {
  666 + return 1;
  667 + }
  668 + return 0;
715 } 669 }
716 670
717 int 671 int
718 NNTreeImpl::binarySearch( 672 NNTreeImpl::binarySearch(
719 QPDFObjectHandle key, 673 QPDFObjectHandle key,
720 QPDFObjectHandle items, 674 QPDFObjectHandle items,
721 - int num_items, 675 + size_t num_items,
722 bool return_prev_if_not_found, 676 bool return_prev_if_not_found,
723 int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)) 677 int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item))
724 { 678 {
725 - int max_idx = 1;  
726 - while (max_idx < num_items) {  
727 - max_idx <<= 1;  
728 - } 679 + size_t max_idx = std::bit_ceil(num_items);
729 680
730 - int step = max_idx / 2;  
731 - int checks = max_idx; 681 + int step = static_cast<int>(max_idx / 2);
  682 + size_t checks = max_idx;
732 int idx = step; 683 int idx = step;
733 int found_idx = -1; 684 int found_idx = -1;
734 - bool found = false;  
735 - bool found_leq = false;  
736 - int status = 0;  
737 685
738 - while ((!found) && (checks > 0)) {  
739 - if (idx < num_items) { 686 + while (checks > 0) {
  687 + int status = -1;
  688 + if (std::cmp_less(idx, num_items)) {
740 status = (this->*compare)(key, items, idx); 689 status = (this->*compare)(key, items, idx);
741 - if (status >= 0) {  
742 - found_leq = true; 690 + if (status == 0) {
  691 + return idx;
  692 + }
  693 + if (status > 0) {
743 found_idx = idx; 694 found_idx = idx;
744 } 695 }
745 - } else {  
746 - // consider item to be below anything after the top  
747 - status = -1;  
748 } 696 }
  697 + checks >>= 1;
  698 + if (checks > 0) {
  699 + step >>= 1;
  700 + if (step == 0) {
  701 + step = 1;
  702 + }
749 703
750 - if (status == 0) {  
751 - found = true;  
752 - } else {  
753 - checks >>= 1;  
754 - if (checks > 0) {  
755 - step >>= 1;  
756 - if (step == 0) {  
757 - step = 1;  
758 - }  
759 -  
760 - if (status < 0) {  
761 - idx -= step;  
762 - } else {  
763 - idx += step;  
764 - } 704 + if (status < 0) {
  705 + idx -= step;
  706 + } else {
  707 + idx += step;
765 } 708 }
766 } 709 }
767 } 710 }
768 711
769 - if (found || (found_leq && return_prev_if_not_found)) {  
770 - return found_idx;  
771 - } else {  
772 - return -1;  
773 - } 712 + return return_prev_if_not_found ? found_idx : -1;
774 } 713 }
775 714
776 int 715 int
777 NNTreeImpl::compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx) 716 NNTreeImpl::compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx)
778 { 717 {
779 - if (!((items.isArray() && (items.getArrayNItems() > (2 * idx)) &&  
780 - details.keyValid(items.getArrayItem(2 * idx))))) {  
781 - QTC::TC("qpdf", "NNTree item is wrong type"); 718 + if (!(std::cmp_greater(items.size(), 2 * idx) && details.keyValid(items[2 * idx]))) {
782 error(oh, ("item at index " + std::to_string(2 * idx) + " is not the right type")); 719 error(oh, ("item at index " + std::to_string(2 * idx) + " is not the right type"));
783 } 720 }
784 - return details.compareKeys(key, items.getArrayItem(2 * idx)); 721 + return details.compareKeys(key, items[2 * idx]);
785 } 722 }
786 723
787 int 724 int
788 NNTreeImpl::compareKeyKid(QPDFObjectHandle& key, QPDFObjectHandle& kids, int idx) 725 NNTreeImpl::compareKeyKid(QPDFObjectHandle& key, QPDFObjectHandle& kids, int idx)
789 { 726 {
790 - if (!(kids.isArray() && (idx < kids.getArrayNItems()) &&  
791 - kids.getArrayItem(idx).isDictionary())) {  
792 - QTC::TC("qpdf", "NNTree kid is invalid"); 727 + if (!(std::cmp_less(idx, kids.size()) && kids[idx].isDictionary())) {
793 error(oh, "invalid kid at index " + std::to_string(idx)); 728 error(oh, "invalid kid at index " + std::to_string(idx));
794 } 729 }
795 - return withinLimits(key, kids.getArrayItem(idx)); 730 + return withinLimits(key, kids[idx]);
796 } 731 }
797 732
798 void 733 void
@@ -826,28 +761,19 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) @@ -826,28 +761,19 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found)
826 } 761 }
827 762
828 NNTreeImpl::iterator 763 NNTreeImpl::iterator
829 -NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) 764 +NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found)
830 { 765 {
831 auto first_item = begin(); 766 auto first_item = begin();
832 auto last_item = end(); 767 auto last_item = end();
833 if (first_item == end()) { 768 if (first_item == end()) {
834 - // Empty  
835 return end(); 769 return end();
836 - } else if (  
837 - first_item.valid() && details.keyValid(first_item->first) && 770 + }
  771 + if (first_item.valid() && details.keyValid(first_item->first) &&
838 details.compareKeys(key, first_item->first) < 0) { 772 details.compareKeys(key, first_item->first) < 0) {
839 // Before the first key 773 // Before the first key
840 return end(); 774 return end();
841 - } else if (  
842 - last_item.valid() && details.keyValid(last_item->first) &&  
843 - details.compareKeys(key, last_item->first) > 0) {  
844 - // After the last key  
845 - if (return_prev_if_not_found) {  
846 - return last_item;  
847 - } else {  
848 - return end();  
849 - }  
850 } 775 }
  776 + qpdf_assert_debug(!last_item.valid());
851 777
852 QPDFObjGen::set seen; 778 QPDFObjGen::set seen;
853 auto node = oh; 779 auto node = oh;
@@ -855,48 +781,44 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) @@ -855,48 +781,44 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found)
855 781
856 while (true) { 782 while (true) {
857 if (!seen.add(node)) { 783 if (!seen.add(node)) {
858 - QTC::TC("qpdf", "NNTree loop in find");  
859 error(node, "loop detected in find"); 784 error(node, "loop detected in find");
860 } 785 }
861 786
862 - auto kids = node.getKey("/Kids");  
863 - int nkids = kids.isArray() ? kids.getArrayNItems() : 0;  
864 auto items = node.getKey(details.itemsKey()); 787 auto items = node.getKey(details.itemsKey());
865 - int nitems = items.isArray() ? items.getArrayNItems() : 0;  
866 - if (nitems > 0) { 788 + size_t nitems = items.size();
  789 + if (nitems > 1) {
867 int idx = binarySearch( 790 int idx = binarySearch(
868 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); 791 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem);
869 if (idx >= 0) { 792 if (idx >= 0) {
870 result.setItemNumber(node, 2 * idx); 793 result.setItemNumber(node, 2 * idx);
871 } 794 }
872 - break;  
873 - } else if (nkids > 0) { 795 + return result;
  796 + }
  797 +
  798 + auto kids = node.getKey("/Kids");
  799 + size_t nkids = kids.isArray() ? kids.size() : 0;
  800 + if (nkids > 0) {
874 int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid); 801 int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid);
875 if (idx == -1) { 802 if (idx == -1) {
876 - QTC::TC("qpdf", "NNTree -1 in binary search");  
877 error(node, "unexpected -1 from binary search of kids; limits may by wrong"); 803 error(node, "unexpected -1 from binary search of kids; limits may by wrong");
878 } 804 }
879 result.addPathElement(node, idx); 805 result.addPathElement(node, idx);
880 - node = kids.getArrayItem(idx); 806 + node = kids[idx];
881 } else { 807 } else {
882 - QTC::TC("qpdf", "NNTree bad node during find");  
883 error(node, "bad node during find"); 808 error(node, "bad node during find");
884 } 809 }
885 } 810 }
886 -  
887 - return result;  
888 } 811 }
889 812
890 NNTreeImpl::iterator 813 NNTreeImpl::iterator
891 -NNTreeImpl::insertFirst(QPDFObjectHandle key, QPDFObjectHandle value) 814 +NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value)
892 { 815 {
893 auto iter = begin(); 816 auto iter = begin();
894 QPDFObjectHandle items; 817 QPDFObjectHandle items;
895 if (iter.node.isDictionary()) { 818 if (iter.node.isDictionary()) {
896 items = iter.node.getKey(details.itemsKey()); 819 items = iter.node.getKey(details.itemsKey());
897 } 820 }
898 - if (!(items.isArray())) {  
899 - QTC::TC("qpdf", "NNTree no valid items node in insertFirst"); 821 + if (!items.isArray()) {
900 error(oh, "unable to find a valid items node"); 822 error(oh, "unable to find a valid items node");
901 } 823 }
902 items.insertItem(0, key); 824 items.insertItem(0, key);
@@ -908,30 +830,26 @@ NNTreeImpl::insertFirst(QPDFObjectHandle key, QPDFObjectHandle value) @@ -908,30 +830,26 @@ NNTreeImpl::insertFirst(QPDFObjectHandle key, QPDFObjectHandle value)
908 } 830 }
909 831
910 NNTreeImpl::iterator 832 NNTreeImpl::iterator
911 -NNTreeImpl::insert(QPDFObjectHandle key, QPDFObjectHandle value) 833 +NNTreeImpl::insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value)
912 { 834 {
913 auto iter = find(key, true); 835 auto iter = find(key, true);
914 if (!iter.valid()) { 836 if (!iter.valid()) {
915 - QTC::TC("qpdf", "NNTree insert inserts first");  
916 return insertFirst(key, value); 837 return insertFirst(key, value);
917 } else if (details.compareKeys(key, iter->first) == 0) { 838 } else if (details.compareKeys(key, iter->first) == 0) {
918 - QTC::TC("qpdf", "NNTree insert replaces");  
919 auto items = iter.node.getKey(details.itemsKey()); 839 auto items = iter.node.getKey(details.itemsKey());
920 items.setArrayItem(iter.item_number + 1, value); 840 items.setArrayItem(iter.item_number + 1, value);
921 iter.updateIValue(); 841 iter.updateIValue();
922 } else { 842 } else {
923 - QTC::TC("qpdf", "NNTree insert inserts after");  
924 iter.insertAfter(key, value); 843 iter.insertAfter(key, value);
925 } 844 }
926 return iter; 845 return iter;
927 } 846 }
928 847
929 bool 848 bool
930 -NNTreeImpl::remove(QPDFObjectHandle key, QPDFObjectHandle* value) 849 +NNTreeImpl::remove(QPDFObjectHandle const& key, QPDFObjectHandle* value)
931 { 850 {
932 auto iter = find(key, false); 851 auto iter = find(key, false);
933 if (!iter.valid()) { 852 if (!iter.valid()) {
934 - QTC::TC("qpdf", "NNTree remove not found");  
935 return false; 853 return false;
936 } 854 }
937 if (value) { 855 if (value) {
libqpdf/QPDF_Array.cc
@@ -528,3 +528,47 @@ BaseHandle::size() const @@ -528,3 +528,47 @@ BaseHandle::size() const
528 return 0; // unreachable 528 return 0; // unreachable
529 } 529 }
530 } 530 }
  531 +
  532 +QPDFObjectHandle
  533 +BaseHandle::operator[](size_t n) const
  534 +{
  535 + switch (resolved_type_code()) {
  536 + case ::ot_array:
  537 + {
  538 + auto a = as<QPDF_Array>();
  539 + if (n >= a->size()) {
  540 + return {};
  541 + }
  542 + return Array(obj).at(static_cast<int>(n)).second;
  543 + }
  544 + case ::ot_uninitialized:
  545 + case ::ot_reserved:
  546 + case ::ot_null:
  547 + case ::ot_destroyed:
  548 + case ::ot_unresolved:
  549 + case ::ot_reference:
  550 + return {};
  551 + case ::ot_boolean:
  552 + case ::ot_integer:
  553 + case ::ot_real:
  554 + case ::ot_string:
  555 + case ::ot_name:
  556 + case ::ot_dictionary:
  557 + case ::ot_stream:
  558 + case ::ot_inlineimage:
  559 + case ::ot_operator:
  560 + return {obj};
  561 + default:
  562 + throw std::logic_error("Unexpected type code in size"); // unreachable
  563 + return {}; // unreachable
  564 + }
  565 +}
  566 +
  567 +QPDFObjectHandle
  568 +BaseHandle::operator[](int n) const
  569 +{
  570 + if (n < 0) {
  571 + return {};
  572 + }
  573 + return (*this)[static_cast<size_t>(n)];
  574 +}
libqpdf/qpdf/NNTree.hh
@@ -56,7 +56,7 @@ class NNTreeIterator @@ -56,7 +56,7 @@ class NNTreeIterator
56 return !operator==(other); 56 return !operator==(other);
57 } 57 }
58 58
59 - void insertAfter(QPDFObjectHandle key, QPDFObjectHandle value); 59 + void insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& value);
60 void remove(); 60 void remove();
61 61
62 private: 62 private:
@@ -100,9 +100,9 @@ class NNTreeImpl @@ -100,9 +100,9 @@ class NNTreeImpl
100 iterator end(); 100 iterator end();
101 iterator last(); 101 iterator last();
102 iterator find(QPDFObjectHandle key, bool return_prev_if_not_found = false); 102 iterator find(QPDFObjectHandle key, bool return_prev_if_not_found = false);
103 - iterator insertFirst(QPDFObjectHandle key, QPDFObjectHandle value);  
104 - iterator insert(QPDFObjectHandle key, QPDFObjectHandle value);  
105 - bool remove(QPDFObjectHandle key, QPDFObjectHandle* value = nullptr); 103 + iterator insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value);
  104 + iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value);
  105 + bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr);
106 106
107 // Change the split threshold for easier testing. There's no real reason to expose this to 107 // Change the split threshold for easier testing. There's no real reason to expose this to
108 // downstream tree helpers, but it has to be public so we can call it from the test suite. 108 // downstream tree helpers, but it has to be public so we can call it from the test suite.
@@ -110,18 +110,18 @@ class NNTreeImpl @@ -110,18 +110,18 @@ class NNTreeImpl
110 110
111 private: 111 private:
112 void repair(); 112 void repair();
113 - iterator findInternal(QPDFObjectHandle key, bool return_prev_if_not_found = false);  
114 - int withinLimits(QPDFObjectHandle key, QPDFObjectHandle node); 113 + iterator findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found = false);
  114 + int withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node);
115 int binarySearch( 115 int binarySearch(
116 QPDFObjectHandle key, 116 QPDFObjectHandle key,
117 QPDFObjectHandle items, 117 QPDFObjectHandle items,
118 - int num_items, 118 + size_t num_items,
119 bool return_prev_if_not_found, 119 bool return_prev_if_not_found,
120 int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)); 120 int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item));
121 int compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); 121 int compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx);
122 int compareKeyKid(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); 122 int compareKeyKid(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx);
123 - void warn(QPDFObjectHandle& node, std::string const& msg);  
124 - void error(QPDFObjectHandle& node, std::string const& msg); 123 + void warn(QPDFObjectHandle const& node, std::string const& msg);
  124 + void error(QPDFObjectHandle const& node, std::string const& msg);
125 125
126 NNTreeDetails const& details; 126 NNTreeDetails const& details;
127 QPDF& qpdf; 127 QPDF& qpdf;
qpdf/qpdf.testcov
@@ -519,50 +519,9 @@ qpdf-c called qpdf_oh_unparse_resolved 0 @@ -519,50 +519,9 @@ qpdf-c called qpdf_oh_unparse_resolved 0
519 qpdf-c called qpdf_oh_unparse_binary 0 519 qpdf-c called qpdf_oh_unparse_binary 0
520 QPDFWriter getFilterOnWrite false 0 520 QPDFWriter getFilterOnWrite false 0
521 QPDFPageObjectHelper::forEachXObject 3 521 QPDFPageObjectHelper::forEachXObject 3
522 -NNTree deepen: invalid node 0  
523 -NNTree deepen: loop 0  
524 -NNTree skip invalid kid 0  
525 -NNTree skip item at end of short items 0  
526 -NNTree skip invalid key 0  
527 -NNTree no valid items node in insertFirst 0  
528 -NNTree deepen found empty 0  
529 -NNTree insert inserts first 0  
530 -NNTree insert replaces 0  
531 -NNTree insert inserts after 0  
532 -NNTree unable to determine limits 0  
533 -NNTree warn indirect kid 0  
534 -NNTree fix indirect kid 0  
535 NNTree repair 0 522 NNTree repair 0
536 -NNTree split root + leaf 0  
537 -NNTree split root + !leaf 0  
538 -NNTree split kids 0  
539 -NNTree split items 0  
540 -NNTree split second half item 0  
541 -NNTree split parent 0  
542 -NNTree split second half kid 0  
543 -NNTree missing limits 0  
544 -NNTree item is wrong type 0  
545 -NNTree kid is invalid 0  
546 -NNTree loop in find 0  
547 -NNTree -1 in binary search 0  
548 -NNTree bad node during find 0  
549 -NNTree node is not a dictionary 0  
550 -NNTree limits didn't change 0  
551 -NNTree increment end() 0  
552 NNTree insertAfter inserts first 0 523 NNTree insertAfter inserts first 0
553 -NNTree remove not found 0  
554 -NNTree remove reset limits 0  
555 -NNTree erased last item 0  
556 -NNTree erased non-last item 0  
557 -NNTree items is empty after remove 0  
558 -NNTree erased all items on leaf/root 0  
559 -NNTree erased first or last kid 0  
560 -NNTree erased last kid 0  
561 -NNTree erased non-last kid 0  
562 -NNTree non-flat tree is empty after remove 0  
563 -NNTree remove walking up tree 0  
564 -NNTree erased last item in tree 0  
565 -NNTree remove limits from root 0 524 +NNTree erased last kid/item in tree 1
566 QPDFPageObjectHelper unresolved names 0 525 QPDFPageObjectHelper unresolved names 0
567 QPDFPageObjectHelper resolving unresolved 0 526 QPDFPageObjectHelper resolving unresolved 0
568 QPDFFileSpecObjectHelper empty compat_name 0 527 QPDFFileSpecObjectHelper empty compat_name 0