Commit db288584617600ba09f5ebbd59bf14fee4d6b6c7
Committed by
GitHub
Merge pull request #1522 from m-holger/oh_array
Add private-API subscript operators to `QPDFObjectHandle`
Showing
5 changed files
with
206 additions
and
281 deletions
include/qpdf/ObjectHandle.hh
| ... | ... | @@ -73,6 +73,10 @@ namespace qpdf |
| 73 | 73 | return size() == 0; |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | + QPDFObjectHandle operator[](size_t n) const; | |
| 77 | + | |
| 78 | + QPDFObjectHandle operator[](int n) const; | |
| 79 | + | |
| 76 | 80 | std::shared_ptr<QPDFObject> copy(bool shallow = false) const; |
| 77 | 81 | // Recursively remove association with any QPDF object. This method may only be called |
| 78 | 82 | // during final destruction. | ... | ... |
libqpdf/NNTree.cc
| 1 | +#include <qpdf/assert_debug.h> | |
| 2 | + | |
| 1 | 3 | #include <qpdf/NNTree.hh> |
| 2 | 4 | |
| 5 | +#include <qpdf/QPDFObjectHandle_private.hh> | |
| 3 | 6 | #include <qpdf/QPDF_private.hh> |
| 4 | 7 | #include <qpdf/QTC.hh> |
| 5 | 8 | #include <qpdf/QUtil.hh> |
| 6 | 9 | |
| 10 | +#include <bit> | |
| 7 | 11 | #include <exception> |
| 12 | +#include <utility> | |
| 8 | 13 | |
| 9 | 14 | static std::string |
| 10 | -get_description(QPDFObjectHandle& node) | |
| 15 | +get_description(QPDFObjectHandle const& node) | |
| 11 | 16 | { |
| 12 | 17 | std::string result("Name/Number tree node"); |
| 13 | - if (node.isIndirect()) { | |
| 18 | + if (node.indirect()) { | |
| 14 | 19 | result += " (object " + std::to_string(node.getObjectID()) + ")"; |
| 15 | 20 | } |
| 16 | 21 | return result; |
| 17 | 22 | } |
| 18 | 23 | |
| 19 | 24 | void |
| 20 | -NNTreeImpl::warn(QPDFObjectHandle& node, std::string const& msg) | |
| 25 | +NNTreeImpl::warn(QPDFObjectHandle const& node, std::string const& msg) | |
| 21 | 26 | { |
| 22 | 27 | qpdf.warn(qpdf_e_damaged_pdf, get_description(node), 0, msg); |
| 23 | 28 | if (++error_count > 5 && qpdf.reconstructed_xref()) { |
| ... | ... | @@ -26,7 +31,7 @@ NNTreeImpl::warn(QPDFObjectHandle& node, std::string const& msg) |
| 26 | 31 | } |
| 27 | 32 | |
| 28 | 33 | void |
| 29 | -NNTreeImpl::error(QPDFObjectHandle& node, std::string const& msg) | |
| 34 | +NNTreeImpl::error(QPDFObjectHandle const& node, std::string const& msg) | |
| 30 | 35 | { |
| 31 | 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 | 57 | // we must call updateIValue as well. These cases are handled, and for good measure, we also |
| 53 | 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 | 61 | if (!allow_invalid) { |
| 68 | 62 | throw std::logic_error( |
| 69 | 63 | "attempt made to dereference an invalid name/number tree iterator"); |
| 70 | 64 | } |
| 71 | 65 | ivalue.first = QPDFObjectHandle(); |
| 72 | 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 | 77 | NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const& node, int kid_number) : |
| ... | ... | @@ -82,28 +83,22 @@ NNTreeIterator::PathElement::PathElement(QPDFObjectHandle const& node, int kid_n |
| 82 | 83 | QPDFObjectHandle |
| 83 | 84 | NNTreeIterator::getNextKid(PathElement& pe, bool backward) |
| 84 | 85 | { |
| 85 | - QPDFObjectHandle result; | |
| 86 | - bool found = false; | |
| 87 | - while (!found) { | |
| 86 | + while (true) { | |
| 88 | 87 | pe.kid_number += backward ? -1 : 1; |
| 89 | 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 | 91 | if (result.isDictionary() && |
| 93 | 92 | (result.hasKey("/Kids") || result.hasKey(impl.details.itemsKey()))) { |
| 94 | - found = true; | |
| 93 | + return result; | |
| 95 | 94 | } else { |
| 96 | - QTC::TC("qpdf", "NNTree skip invalid kid"); | |
| 97 | 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 | 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 | 104 | bool |
| ... | ... | @@ -116,21 +111,20 @@ void |
| 116 | 111 | NNTreeIterator::increment(bool backward) |
| 117 | 112 | { |
| 118 | 113 | if (item_number < 0) { |
| 119 | - QTC::TC("qpdf", "NNTree increment end()"); | |
| 120 | 114 | deepen(impl.oh, !backward, true); |
| 121 | 115 | return; |
| 122 | 116 | } |
| 123 | - bool found_valid_key = false; | |
| 124 | - while (valid() && !found_valid_key) { | |
| 117 | + | |
| 118 | + while (valid()) { | |
| 125 | 119 | item_number += backward ? -2 : 2; |
| 126 | 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 | 122 | bool found = false; |
| 129 | 123 | setItemNumber(QPDFObjectHandle(), -1); |
| 130 | 124 | while (!(found || path.empty())) { |
| 131 | 125 | auto& element = path.back(); |
| 132 | 126 | auto pe_node = getNextKid(element, backward); |
| 133 | - if (pe_node.isNull()) { | |
| 127 | + if (pe_node.null()) { | |
| 134 | 128 | path.pop_back(); |
| 135 | 129 | } else { |
| 136 | 130 | found = deepen(pe_node, !backward, false); |
| ... | ... | @@ -139,14 +133,12 @@ NNTreeIterator::increment(bool backward) |
| 139 | 133 | } |
| 140 | 134 | if (item_number >= 0) { |
| 141 | 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 | 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 | 139 | impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type")); |
| 148 | 140 | } else { |
| 149 | - found_valid_key = true; | |
| 141 | + return; | |
| 150 | 142 | } |
| 151 | 143 | } |
| 152 | 144 | } |
| ... | ... | @@ -155,35 +147,31 @@ NNTreeIterator::increment(bool backward) |
| 155 | 147 | void |
| 156 | 148 | NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list<PathElement>::iterator parent) |
| 157 | 149 | { |
| 158 | - bool done = false; | |
| 159 | - while (!done) { | |
| 150 | + while (true) { | |
| 160 | 151 | if (parent == path.end()) { |
| 161 | - QTC::TC("qpdf", "NNTree remove limits from root"); | |
| 162 | 152 | a_node.removeKey("/Limits"); |
| 163 | - done = true; | |
| 164 | 153 | break; |
| 165 | 154 | } |
| 166 | 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 | 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 | 160 | bool changed = true; |
| 172 | 161 | QPDFObjectHandle first; |
| 173 | 162 | QPDFObjectHandle last; |
| 174 | 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 | 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 | 169 | if (first_kid.isDictionary() && last_kid.isDictionary()) { |
| 181 | 170 | auto first_limits = first_kid.getKey("/Limits"); |
| 182 | 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<PathElement>::ite |
| 192 | 180 | limits.appendItem(first); |
| 193 | 181 | limits.appendItem(last); |
| 194 | 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 | 186 | if (impl.details.keyValid(ofirst) && impl.details.keyValid(olast) && |
| 199 | 187 | (impl.details.compareKeys(first, ofirst) == 0) && |
| 200 | 188 | (impl.details.compareKeys(last, olast) == 0)) { |
| 201 | - QTC::TC("qpdf", "NNTree limits didn't change"); | |
| 202 | 189 | changed = false; |
| 203 | 190 | } |
| 204 | 191 | } |
| ... | ... | @@ -206,12 +193,11 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list<PathElement>::ite |
| 206 | 193 | a_node.replaceKey("/Limits", limits); |
| 207 | 194 | } |
| 208 | 195 | } else { |
| 209 | - QTC::TC("qpdf", "NNTree unable to determine limits"); | |
| 210 | 196 | impl.warn(a_node, "unable to determine limits"); |
| 211 | 197 | } |
| 212 | 198 | |
| 213 | 199 | if (!changed || parent == path.begin()) { |
| 214 | - done = true; | |
| 200 | + break; | |
| 215 | 201 | } else { |
| 216 | 202 | a_node = parent->node; |
| 217 | 203 | --parent; |
| ... | ... | @@ -255,22 +241,20 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato |
| 255 | 241 | |
| 256 | 242 | // Find the array we actually need to split, which is either this node's kids or items. |
| 257 | 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 | 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 | 248 | QPDFObjectHandle first_half; |
| 263 | 249 | int n = 0; |
| 264 | 250 | std::string key; |
| 265 | 251 | int threshold = 0; |
| 266 | 252 | if (nkids > 0) { |
| 267 | - QTC::TC("qpdf", "NNTree split kids"); | |
| 268 | 253 | first_half = kids; |
| 269 | 254 | n = nkids; |
| 270 | 255 | threshold = impl.split_threshold; |
| 271 | 256 | key = "/Kids"; |
| 272 | 257 | } else if (nitems > 0) { |
| 273 | - QTC::TC("qpdf", "NNTree split items"); | |
| 274 | 258 | first_half = items; |
| 275 | 259 | n = nitems; |
| 276 | 260 | threshold = 2 * impl.split_threshold; |
| ... | ... | @@ -283,8 +267,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato |
| 283 | 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 | 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<PathElement>::iterato |
| 311 | 295 | to_split.removeKey(impl.details.itemsKey()); |
| 312 | 296 | to_split.replaceKey("/Kids", new_kids); |
| 313 | 297 | if (is_leaf) { |
| 314 | - QTC::TC("qpdf", "NNTree split root + leaf"); | |
| 315 | 298 | node = first_node; |
| 316 | 299 | } else { |
| 317 | - QTC::TC("qpdf", "NNTree split root + !leaf"); | |
| 318 | 300 | auto next = path.begin(); |
| 319 | 301 | next->node = first_node; |
| 320 | 302 | } |
| ... | ... | @@ -330,8 +312,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato |
| 330 | 312 | // array. |
| 331 | 313 | QPDFObjectHandle second_half = QPDFObjectHandle::newArray(); |
| 332 | 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 | 317 | first_half.eraseItem(start_idx); |
| 336 | 318 | } |
| 337 | 319 | resetLimits(to_split, parent); |
| ... | ... | @@ -357,16 +339,13 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list<PathElement>::iterato |
| 357 | 339 | if (old_idx >= start_idx) { |
| 358 | 340 | ++parent->kid_number; |
| 359 | 341 | if (is_leaf) { |
| 360 | - QTC::TC("qpdf", "NNTree split second half item"); | |
| 361 | 342 | setItemNumber(second_node, item_number - start_idx); |
| 362 | 343 | } else { |
| 363 | - QTC::TC("qpdf", "NNTree split second half kid"); | |
| 364 | 344 | cur_elem->node = second_node; |
| 365 | 345 | cur_elem->kid_number -= start_idx; |
| 366 | 346 | } |
| 367 | 347 | } |
| 368 | 348 | if (!is_root) { |
| 369 | - QTC::TC("qpdf", "NNTree split parent"); | |
| 370 | 349 | auto next = parent->node; |
| 371 | 350 | resetLimits(next, parent); |
| 372 | 351 | --parent; |
| ... | ... | @@ -385,7 +364,7 @@ NNTreeIterator::lastPathElement() |
| 385 | 364 | } |
| 386 | 365 | |
| 387 | 366 | void |
| 388 | -NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) | |
| 367 | +NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& value) | |
| 389 | 368 | { |
| 390 | 369 | if (!valid()) { |
| 391 | 370 | QTC::TC("qpdf", "NNTree insertAfter inserts first"); |
| ... | ... | @@ -395,10 +374,11 @@ NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) |
| 395 | 374 | } |
| 396 | 375 | |
| 397 | 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 | 382 | impl.error(node, "insert: items array is too short"); |
| 403 | 383 | } |
| 404 | 384 | items.insertItem(item_number + 2, key); |
| ... | ... | @@ -417,8 +397,8 @@ NNTreeIterator::remove() |
| 417 | 397 | throw std::logic_error("attempt made to remove an invalid iterator"); |
| 418 | 398 | } |
| 419 | 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 | 402 | impl.error(node, "found short items array while removing an item"); |
| 423 | 403 | } |
| 424 | 404 | |
| ... | ... | @@ -432,20 +412,17 @@ NNTreeIterator::remove() |
| 432 | 412 | if (item_number == 0 || item_number == nitems) { |
| 433 | 413 | // We removed either the first or last item of an items array that remains non-empty, so |
| 434 | 414 | // we have to adjust limits. |
| 435 | - QTC::TC("qpdf", "NNTree remove reset limits"); | |
| 436 | 415 | resetLimits(node, lastPathElement()); |
| 437 | 416 | } |
| 438 | 417 | |
| 439 | 418 | if (item_number == nitems) { |
| 440 | 419 | // We removed the last item of a non-empty items array, so advance to the successor of |
| 441 | 420 | // the previous item. |
| 442 | - QTC::TC("qpdf", "NNTree erased last item"); | |
| 443 | 421 | item_number -= 2; |
| 444 | 422 | increment(false); |
| 445 | 423 | } else if (item_number < nitems) { |
| 446 | 424 | // We don't have to do anything since the removed item's successor now occupies its |
| 447 | 425 | // former location. |
| 448 | - QTC::TC("qpdf", "NNTree erased non-last item"); | |
| 449 | 426 | updateIValue(); |
| 450 | 427 | } else { |
| 451 | 428 | // We already checked to ensure this condition would not happen. |
| ... | ... | @@ -456,61 +433,51 @@ NNTreeIterator::remove() |
| 456 | 433 | |
| 457 | 434 | if (path.empty()) { |
| 458 | 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 | 436 | setItemNumber(impl.oh, -1); |
| 461 | 437 | return; |
| 462 | 438 | } |
| 463 | 439 | |
| 464 | - QTC::TC("qpdf", "NNTree items is empty after remove"); | |
| 465 | - | |
| 466 | 440 | // We removed the last item from this items array, so we need to remove this node from the |
| 467 | 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 | 443 | auto element = lastPathElement(); |
| 471 | 444 | auto parent = element; |
| 472 | 445 | --parent; |
| 473 | 446 | auto kids = element->node.getKey("/Kids"); |
| 474 | 447 | kids.eraseItem(element->kid_number); |
| 475 | - auto nkids = kids.getArrayNItems(); | |
| 448 | + auto nkids = kids.size(); | |
| 476 | 449 | if (nkids > 0) { |
| 477 | 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 | 452 | resetLimits(element->node, parent); |
| 481 | 453 | } |
| 482 | - if (element->kid_number == nkids) { | |
| 454 | + if (std::cmp_equal(element->kid_number, nkids)) { | |
| 483 | 455 | // Move to the successor of the last child of the previous kid. |
| 484 | - setItemNumber(QPDFObjectHandle(), -1); | |
| 456 | + setItemNumber({}, -1); | |
| 485 | 457 | --element->kid_number; |
| 486 | - deepen(kids.getArrayItem(element->kid_number), false, true); | |
| 458 | + deepen(kids[element->kid_number], false, true); | |
| 487 | 459 | if (valid()) { |
| 488 | 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 | 463 | } else { |
| 496 | 464 | // Next kid is in deleted kid's position |
| 497 | - QTC::TC("qpdf", "NNTree erased non-last kid"); | |
| 498 | 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 | 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 | 472 | element->node.removeKey("/Kids"); |
| 505 | 473 | element->node.replaceKey(impl.details.itemsKey(), QPDFObjectHandle::newArray()); |
| 506 | 474 | path.clear(); |
| 507 | 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 | 554 | // items. If we succeed, return true; otherwise return false and leave path alone. |
| 588 | 555 | |
| 589 | 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 | 564 | QPDFObjGen::set seen; |
| 593 | 565 | for (auto const& i: path) { |
| 594 | 566 | seen.add(i.node); |
| 595 | 567 | } |
| 596 | - while (!failed) { | |
| 568 | + while (true) { | |
| 597 | 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 | 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 | 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 | 580 | setItemNumber(a_node, first ? 0 : nitems - 2); |
| 617 | 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 | 587 | int kid_number = first ? 0 : nkids - 1; |
| 620 | 588 | addPathElement(a_node, kid_number); |
| 621 | - auto next = kids.getArrayItem(kid_number); | |
| 589 | + auto next = kids[kid_number]; | |
| 622 | 590 | if (!next.isIndirect()) { |
| 623 | 591 | if (impl.auto_repair) { |
| 624 | - QTC::TC("qpdf", "NNTree fix indirect kid"); | |
| 625 | 592 | impl.warn( |
| 626 | 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 | 596 | next = impl.qpdf.makeIndirectObject(next); |
| 630 | 597 | kids.setArrayItem(kid_number, next); |
| 631 | 598 | } else { |
| 632 | - QTC::TC("qpdf", "NNTree warn indirect kid"); | |
| 633 | 599 | impl.warn( |
| 634 | 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 | 604 | a_node = next; |
| 640 | 605 | } else if (allow_empty && items.isArray()) { |
| 641 | - QTC::TC("qpdf", "NNTree deepen found empty"); | |
| 642 | 606 | setItemNumber(a_node, -1); |
| 643 | 607 | break; |
| 644 | 608 | } else { |
| 645 | - QTC::TC("qpdf", "NNTree deepen: invalid node"); | |
| 646 | - impl.warn( | |
| 609 | + return fail( | |
| 647 | 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 | 615 | return true; |
| 659 | 616 | } |
| 660 | 617 | |
| ... | ... | @@ -696,103 +653,81 @@ NNTreeImpl::last() |
| 696 | 653 | } |
| 697 | 654 | |
| 698 | 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 | 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 | 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 | 671 | int |
| 718 | 672 | NNTreeImpl::binarySearch( |
| 719 | 673 | QPDFObjectHandle key, |
| 720 | 674 | QPDFObjectHandle items, |
| 721 | - int num_items, | |
| 675 | + size_t num_items, | |
| 722 | 676 | bool return_prev_if_not_found, |
| 723 | 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 | 683 | int idx = step; |
| 733 | 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 | 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 | 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 | 715 | int |
| 777 | 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 | 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 | 724 | int |
| 788 | 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 | 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 | 733 | void |
| ... | ... | @@ -826,28 +761,19 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) |
| 826 | 761 | } |
| 827 | 762 | |
| 828 | 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 | 766 | auto first_item = begin(); |
| 832 | 767 | auto last_item = end(); |
| 833 | 768 | if (first_item == end()) { |
| 834 | - // Empty | |
| 835 | 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 | 772 | details.compareKeys(key, first_item->first) < 0) { |
| 839 | 773 | // Before the first key |
| 840 | 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 | 778 | QPDFObjGen::set seen; |
| 853 | 779 | auto node = oh; |
| ... | ... | @@ -855,48 +781,44 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) |
| 855 | 781 | |
| 856 | 782 | while (true) { |
| 857 | 783 | if (!seen.add(node)) { |
| 858 | - QTC::TC("qpdf", "NNTree loop in find"); | |
| 859 | 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 | 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 | 790 | int idx = binarySearch( |
| 868 | 791 | key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); |
| 869 | 792 | if (idx >= 0) { |
| 870 | 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 | 801 | int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid); |
| 875 | 802 | if (idx == -1) { |
| 876 | - QTC::TC("qpdf", "NNTree -1 in binary search"); | |
| 877 | 803 | error(node, "unexpected -1 from binary search of kids; limits may by wrong"); |
| 878 | 804 | } |
| 879 | 805 | result.addPathElement(node, idx); |
| 880 | - node = kids.getArrayItem(idx); | |
| 806 | + node = kids[idx]; | |
| 881 | 807 | } else { |
| 882 | - QTC::TC("qpdf", "NNTree bad node during find"); | |
| 883 | 808 | error(node, "bad node during find"); |
| 884 | 809 | } |
| 885 | 810 | } |
| 886 | - | |
| 887 | - return result; | |
| 888 | 811 | } |
| 889 | 812 | |
| 890 | 813 | NNTreeImpl::iterator |
| 891 | -NNTreeImpl::insertFirst(QPDFObjectHandle key, QPDFObjectHandle value) | |
| 814 | +NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value) | |
| 892 | 815 | { |
| 893 | 816 | auto iter = begin(); |
| 894 | 817 | QPDFObjectHandle items; |
| 895 | 818 | if (iter.node.isDictionary()) { |
| 896 | 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 | 822 | error(oh, "unable to find a valid items node"); |
| 901 | 823 | } |
| 902 | 824 | items.insertItem(0, key); |
| ... | ... | @@ -908,30 +830,26 @@ NNTreeImpl::insertFirst(QPDFObjectHandle key, QPDFObjectHandle value) |
| 908 | 830 | } |
| 909 | 831 | |
| 910 | 832 | NNTreeImpl::iterator |
| 911 | -NNTreeImpl::insert(QPDFObjectHandle key, QPDFObjectHandle value) | |
| 833 | +NNTreeImpl::insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value) | |
| 912 | 834 | { |
| 913 | 835 | auto iter = find(key, true); |
| 914 | 836 | if (!iter.valid()) { |
| 915 | - QTC::TC("qpdf", "NNTree insert inserts first"); | |
| 916 | 837 | return insertFirst(key, value); |
| 917 | 838 | } else if (details.compareKeys(key, iter->first) == 0) { |
| 918 | - QTC::TC("qpdf", "NNTree insert replaces"); | |
| 919 | 839 | auto items = iter.node.getKey(details.itemsKey()); |
| 920 | 840 | items.setArrayItem(iter.item_number + 1, value); |
| 921 | 841 | iter.updateIValue(); |
| 922 | 842 | } else { |
| 923 | - QTC::TC("qpdf", "NNTree insert inserts after"); | |
| 924 | 843 | iter.insertAfter(key, value); |
| 925 | 844 | } |
| 926 | 845 | return iter; |
| 927 | 846 | } |
| 928 | 847 | |
| 929 | 848 | bool |
| 930 | -NNTreeImpl::remove(QPDFObjectHandle key, QPDFObjectHandle* value) | |
| 849 | +NNTreeImpl::remove(QPDFObjectHandle const& key, QPDFObjectHandle* value) | |
| 931 | 850 | { |
| 932 | 851 | auto iter = find(key, false); |
| 933 | 852 | if (!iter.valid()) { |
| 934 | - QTC::TC("qpdf", "NNTree remove not found"); | |
| 935 | 853 | return false; |
| 936 | 854 | } |
| 937 | 855 | if (value) { | ... | ... |
libqpdf/QPDF_Array.cc
| ... | ... | @@ -528,3 +528,47 @@ BaseHandle::size() const |
| 528 | 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 | 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 | 60 | void remove(); |
| 61 | 61 | |
| 62 | 62 | private: |
| ... | ... | @@ -100,9 +100,9 @@ class NNTreeImpl |
| 100 | 100 | iterator end(); |
| 101 | 101 | iterator last(); |
| 102 | 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 | 107 | // Change the split threshold for easier testing. There's no real reason to expose this to |
| 108 | 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 | 110 | |
| 111 | 111 | private: |
| 112 | 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 | 115 | int binarySearch( |
| 116 | 116 | QPDFObjectHandle key, |
| 117 | 117 | QPDFObjectHandle items, |
| 118 | - int num_items, | |
| 118 | + size_t num_items, | |
| 119 | 119 | bool return_prev_if_not_found, |
| 120 | 120 | int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)); |
| 121 | 121 | int compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); |
| 122 | 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 | 126 | NNTreeDetails const& details; |
| 127 | 127 | QPDF& qpdf; | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -519,50 +519,9 @@ qpdf-c called qpdf_oh_unparse_resolved 0 |
| 519 | 519 | qpdf-c called qpdf_oh_unparse_binary 0 |
| 520 | 520 | QPDFWriter getFilterOnWrite false 0 |
| 521 | 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 | 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 | 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 | 525 | QPDFPageObjectHelper unresolved names 0 |
| 567 | 526 | QPDFPageObjectHelper resolving unresolved 0 |
| 568 | 527 | QPDFFileSpecObjectHelper empty compat_name 0 | ... | ... |