Commit 1ec18f20c21aea788e31dfcc5eba68b22048611f
1 parent
06db6493
Add new method `BaseHandle::size`
Refactor array handling in `QPDFObjectHandle`: replace `int` with `size_t` for size and index operations, introduce utility functions for type conversions, and simplify sparse array logic.
Showing
6 changed files
with
124 additions
and
62 deletions
include/qpdf/ObjectHandle.hh
| ... | ... | @@ -61,6 +61,11 @@ namespace qpdf |
| 61 | 61 | |
| 62 | 62 | // The rest of the header file is for qpdf internal use only. |
| 63 | 63 | |
| 64 | + // For arrays, return the number of items in the array. | |
| 65 | + // For null-like objects, return 0. | |
| 66 | + // For all other objects, return 1. | |
| 67 | + size_t size() const; | |
| 68 | + | |
| 64 | 69 | std::shared_ptr<QPDFObject> copy(bool shallow = false) const; |
| 65 | 70 | // Recursively remove association with any QPDF object. This method may only be called |
| 66 | 71 | // during final destruction. | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -352,16 +352,15 @@ BaseHandle::unparse() const |
| 352 | 352 | auto const& a = std::get<QPDF_Array>(obj->value); |
| 353 | 353 | std::string result = "[ "; |
| 354 | 354 | if (a.sp) { |
| 355 | - int next = 0; | |
| 356 | - for (auto& item: a.sp->elements) { | |
| 357 | - int key = item.first; | |
| 358 | - for (int j = next; j < key; ++j) { | |
| 355 | + size_t next = 0; | |
| 356 | + for (auto& [key, value]: a.sp->elements) { | |
| 357 | + for (size_t j = next; j < key; ++j) { | |
| 359 | 358 | result += "null "; |
| 360 | 359 | } |
| 361 | - result += item.second.unparse() + " "; | |
| 362 | - next = ++key; | |
| 360 | + result += value.unparse() + " "; | |
| 361 | + next = key + 1; | |
| 363 | 362 | } |
| 364 | - for (int j = next; j < a.sp->size; ++j) { | |
| 363 | + for (size_t j = next; j < a.sp->size; ++j) { | |
| 365 | 364 | result += "null "; |
| 366 | 365 | } |
| 367 | 366 | } else { |
| ... | ... | @@ -467,22 +466,21 @@ BaseHandle::write_json(int json_version, JSON::Writer& p) const |
| 467 | 466 | auto const& a = std::get<QPDF_Array>(obj->value); |
| 468 | 467 | p.writeStart('['); |
| 469 | 468 | if (a.sp) { |
| 470 | - int next = 0; | |
| 471 | - for (auto& item: a.sp->elements) { | |
| 472 | - int key = item.first; | |
| 473 | - for (int j = next; j < key; ++j) { | |
| 469 | + size_t next = 0; | |
| 470 | + for (auto& [key, value]: a.sp->elements) { | |
| 471 | + for (size_t j = next; j < key; ++j) { | |
| 474 | 472 | p.writeNext() << "null"; |
| 475 | 473 | } |
| 476 | 474 | p.writeNext(); |
| 477 | - auto item_og = item.second.getObj()->getObjGen(); | |
| 475 | + auto item_og = value.getObj()->getObjGen(); | |
| 478 | 476 | if (item_og.isIndirect()) { |
| 479 | 477 | p << "\"" << item_og.unparse(' ') << " R\""; |
| 480 | 478 | } else { |
| 481 | - item.second.write_json(json_version, p); | |
| 479 | + value.write_json(json_version, p); | |
| 482 | 480 | } |
| 483 | - next = ++key; | |
| 481 | + next = key + 1; | |
| 484 | 482 | } |
| 485 | - for (int j = next; j < a.sp->size; ++j) { | |
| 483 | + for (size_t j = next; j < a.sp->size; ++j) { | |
| 486 | 484 | p.writeNext() << "null"; |
| 487 | 485 | } |
| 488 | 486 | } else { |
| ... | ... | @@ -1234,7 +1232,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( |
| 1234 | 1232 | all_description = description; |
| 1235 | 1233 | std::vector<QPDFObjectHandle> result; |
| 1236 | 1234 | if (auto array = as_array(strict)) { |
| 1237 | - int n_items = array.size(); | |
| 1235 | + int n_items = static_cast<int>(array.size()); | |
| 1238 | 1236 | for (int i = 0; i < n_items; ++i) { |
| 1239 | 1237 | QPDFObjectHandle item = array.at(i).second; |
| 1240 | 1238 | if (item.isStream()) { | ... | ... |
libqpdf/QPDF_Array.cc
| ... | ... | @@ -2,11 +2,25 @@ |
| 2 | 2 | |
| 3 | 3 | #include <qpdf/QTC.hh> |
| 4 | 4 | |
| 5 | +#include <utility> | |
| 6 | + | |
| 5 | 7 | using namespace std::literals; |
| 6 | 8 | using namespace qpdf; |
| 7 | 9 | |
| 8 | 10 | static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); |
| 9 | 11 | |
| 12 | +static size_t | |
| 13 | +to_s(int n) | |
| 14 | +{ | |
| 15 | + return static_cast<size_t>(n); | |
| 16 | +} | |
| 17 | + | |
| 18 | +static int | |
| 19 | +to_i(size_t n) | |
| 20 | +{ | |
| 21 | + return static_cast<int>(n); | |
| 22 | +} | |
| 23 | + | |
| 10 | 24 | inline void |
| 11 | 25 | Array::checkOwnership(QPDFObjectHandle const& item) const |
| 12 | 26 | { |
| ... | ... | @@ -142,24 +156,24 @@ Array::null() const |
| 142 | 156 | return null_oh; |
| 143 | 157 | } |
| 144 | 158 | |
| 145 | -int | |
| 159 | +size_t | |
| 146 | 160 | Array::size() const |
| 147 | 161 | { |
| 148 | 162 | auto a = array(); |
| 149 | - return a->sp ? a->sp->size : int(a->elements.size()); | |
| 163 | + return a->sp ? a->sp->size : a->elements.size(); | |
| 150 | 164 | } |
| 151 | 165 | |
| 152 | 166 | std::pair<bool, QPDFObjectHandle> |
| 153 | 167 | Array::at(int n) const |
| 154 | 168 | { |
| 155 | 169 | auto a = array(); |
| 156 | - if (n < 0 || n >= size()) { | |
| 170 | + if (n < 0 || std::cmp_greater_equal(n, size())) { | |
| 157 | 171 | return {false, {}}; |
| 158 | 172 | } |
| 159 | 173 | if (!a->sp) { |
| 160 | - return {true, a->elements[size_t(n)]}; | |
| 174 | + return {true, a->elements[to_s(n)]}; | |
| 161 | 175 | } |
| 162 | - auto const& iter = a->sp->elements.find(n); | |
| 176 | + auto const& iter = a->sp->elements.find(to_s(n)); | |
| 163 | 177 | return {true, iter == a->sp->elements.end() ? null() : iter->second}; |
| 164 | 178 | } |
| 165 | 179 | |
| ... | ... | @@ -169,12 +183,12 @@ Array::getAsVector() const |
| 169 | 183 | auto a = array(); |
| 170 | 184 | if (a->sp) { |
| 171 | 185 | std::vector<QPDFObjectHandle> v; |
| 172 | - v.reserve(size_t(size())); | |
| 186 | + v.reserve(size()); | |
| 173 | 187 | for (auto const& item: a->sp->elements) { |
| 174 | - v.resize(size_t(item.first), null_oh); | |
| 188 | + v.resize(item.first, null_oh); | |
| 175 | 189 | v.emplace_back(item.second); |
| 176 | 190 | } |
| 177 | - v.resize(size_t(size()), null_oh); | |
| 191 | + v.resize(size(), null_oh); | |
| 178 | 192 | return v; |
| 179 | 193 | } else { |
| 180 | 194 | return a->elements; |
| ... | ... | @@ -184,15 +198,15 @@ Array::getAsVector() const |
| 184 | 198 | bool |
| 185 | 199 | Array::setAt(int at, QPDFObjectHandle const& oh) |
| 186 | 200 | { |
| 187 | - if (at < 0 || at >= size()) { | |
| 201 | + if (at < 0 || std::cmp_greater_equal(at, size())) { | |
| 188 | 202 | return false; |
| 189 | 203 | } |
| 190 | 204 | auto a = array(); |
| 191 | 205 | checkOwnership(oh); |
| 192 | 206 | if (a->sp) { |
| 193 | - a->sp->elements[at] = oh; | |
| 207 | + a->sp->elements[to_s(at)] = oh; | |
| 194 | 208 | } else { |
| 195 | - a->elements[size_t(at)] = oh; | |
| 209 | + a->elements[to_s(at)] = oh; | |
| 196 | 210 | } |
| 197 | 211 | return true; |
| 198 | 212 | } |
| ... | ... | @@ -210,34 +224,39 @@ Array::setFromVector(std::vector<QPDFObjectHandle> const& v) |
| 210 | 224 | } |
| 211 | 225 | |
| 212 | 226 | bool |
| 213 | -Array::insert(int at, QPDFObjectHandle const& item) | |
| 227 | +Array::insert(int at_i, QPDFObjectHandle const& item) | |
| 214 | 228 | { |
| 215 | 229 | auto a = array(); |
| 216 | - int sz = size(); | |
| 217 | - if (at < 0 || at > sz) { | |
| 218 | - // As special case, also allow insert beyond the end | |
| 230 | + size_t sz = size(); | |
| 231 | + if (at_i < 0) { | |
| 232 | + return false; | |
| 233 | + } | |
| 234 | + size_t at = to_s(at_i); | |
| 235 | + if (at > sz) { | |
| 219 | 236 | return false; |
| 220 | - } else if (at == sz) { | |
| 237 | + } | |
| 238 | + if (at == sz) { | |
| 239 | + // As special case, also allow insert beyond the end | |
| 221 | 240 | push_back(item); |
| 222 | - } else { | |
| 223 | - checkOwnership(item); | |
| 224 | - if (a->sp) { | |
| 225 | - auto iter = a->sp->elements.crbegin(); | |
| 226 | - while (iter != a->sp->elements.crend()) { | |
| 227 | - auto key = (iter++)->first; | |
| 228 | - if (key >= at) { | |
| 229 | - auto nh = a->sp->elements.extract(key); | |
| 230 | - ++nh.key(); | |
| 231 | - a->sp->elements.insert(std::move(nh)); | |
| 232 | - } else { | |
| 233 | - break; | |
| 234 | - } | |
| 241 | + return true; | |
| 242 | + } | |
| 243 | + checkOwnership(item); | |
| 244 | + if (a->sp) { | |
| 245 | + auto iter = a->sp->elements.crbegin(); | |
| 246 | + while (iter != a->sp->elements.crend()) { | |
| 247 | + auto key = (iter++)->first; | |
| 248 | + if (key >= at) { | |
| 249 | + auto nh = a->sp->elements.extract(key); | |
| 250 | + ++nh.key(); | |
| 251 | + a->sp->elements.insert(std::move(nh)); | |
| 252 | + } else { | |
| 253 | + break; | |
| 235 | 254 | } |
| 236 | - a->sp->elements[at] = item.getObj(); | |
| 237 | - ++a->sp->size; | |
| 238 | - } else { | |
| 239 | - a->elements.insert(a->elements.cbegin() + at, item.getObj()); | |
| 240 | 255 | } |
| 256 | + a->sp->elements[at] = item.getObj(); | |
| 257 | + ++a->sp->size; | |
| 258 | + } else { | |
| 259 | + a->elements.insert(a->elements.cbegin() + at_i, item.getObj()); | |
| 241 | 260 | } |
| 242 | 261 | return true; |
| 243 | 262 | } |
| ... | ... | @@ -255,10 +274,14 @@ Array::push_back(QPDFObjectHandle const& item) |
| 255 | 274 | } |
| 256 | 275 | |
| 257 | 276 | bool |
| 258 | -Array::erase(int at) | |
| 277 | +Array::erase(int at_i) | |
| 259 | 278 | { |
| 260 | 279 | auto a = array(); |
| 261 | - if (at < 0 || at >= size()) { | |
| 280 | + if (at_i < 0) { | |
| 281 | + return false; | |
| 282 | + } | |
| 283 | + size_t at = to_s(at_i); | |
| 284 | + if (at >= size()) { | |
| 262 | 285 | return false; |
| 263 | 286 | } |
| 264 | 287 | if (a->sp) { |
| ... | ... | @@ -277,7 +300,7 @@ Array::erase(int at) |
| 277 | 300 | } |
| 278 | 301 | --(a->sp->size); |
| 279 | 302 | } else { |
| 280 | - a->elements.erase(a->elements.cbegin() + at); | |
| 303 | + a->elements.erase(a->elements.cbegin() + at_i); | |
| 281 | 304 | } |
| 282 | 305 | return true; |
| 283 | 306 | } |
| ... | ... | @@ -286,7 +309,7 @@ int |
| 286 | 309 | QPDFObjectHandle::getArrayNItems() const |
| 287 | 310 | { |
| 288 | 311 | if (auto array = as_array(strict)) { |
| 289 | - return array.size(); | |
| 312 | + return to_i(array.size()); | |
| 290 | 313 | } |
| 291 | 314 | typeWarning("array", "treating as empty"); |
| 292 | 315 | QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); |
| ... | ... | @@ -471,7 +494,37 @@ QPDFObjectHandle |
| 471 | 494 | QPDFObjectHandle::eraseItemAndGetOld(int at) |
| 472 | 495 | { |
| 473 | 496 | auto array = as_array(strict); |
| 474 | - auto result = (array && at < array.size() && at >= 0) ? array.at(at).second : newNull(); | |
| 497 | + auto result = | |
| 498 | + (array && std::cmp_less(at, array.size()) && at >= 0) ? array.at(at).second : newNull(); | |
| 475 | 499 | eraseItem(at); |
| 476 | 500 | return result; |
| 477 | 501 | } |
| 502 | + | |
| 503 | +size_t | |
| 504 | +BaseHandle::size() const | |
| 505 | +{ | |
| 506 | + switch (resolved_type_code()) { | |
| 507 | + case ::ot_array: | |
| 508 | + return as<QPDF_Array>()->size(); | |
| 509 | + case ::ot_uninitialized: | |
| 510 | + case ::ot_reserved: | |
| 511 | + case ::ot_null: | |
| 512 | + case ::ot_destroyed: | |
| 513 | + case ::ot_unresolved: | |
| 514 | + case ::ot_reference: | |
| 515 | + return 0; | |
| 516 | + case ::ot_boolean: | |
| 517 | + case ::ot_integer: | |
| 518 | + case ::ot_real: | |
| 519 | + case ::ot_string: | |
| 520 | + case ::ot_name: | |
| 521 | + case ::ot_dictionary: | |
| 522 | + case ::ot_stream: | |
| 523 | + case ::ot_inlineimage: | |
| 524 | + case ::ot_operator: | |
| 525 | + return 1; | |
| 526 | + default: | |
| 527 | + throw std::logic_error("Unexpected type code in size"); // unreachable | |
| 528 | + return 0; // unreachable | |
| 529 | + } | |
| 530 | +} | ... | ... |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| ... | ... | @@ -38,7 +38,7 @@ namespace qpdf |
| 38 | 38 | |
| 39 | 39 | const_reverse_iterator crend(); |
| 40 | 40 | |
| 41 | - int size() const; | |
| 41 | + size_t size() const; | |
| 42 | 42 | std::pair<bool, QPDFObjectHandle> at(int n) const; |
| 43 | 43 | bool setAt(int at, QPDFObjectHandle const& oh); |
| 44 | 44 | bool insert(int at, QPDFObjectHandle const& item); | ... | ... |
libqpdf/qpdf/QPDFObject_private.hh
| ... | ... | @@ -35,8 +35,8 @@ class QPDF_Array final |
| 35 | 35 | private: |
| 36 | 36 | struct Sparse |
| 37 | 37 | { |
| 38 | - int size{0}; | |
| 39 | - std::map<int, QPDFObjectHandle> elements; | |
| 38 | + size_t size{0}; | |
| 39 | + std::map<size_t, QPDFObjectHandle> elements; | |
| 40 | 40 | }; |
| 41 | 41 | |
| 42 | 42 | public: |
| ... | ... | @@ -65,10 +65,10 @@ class QPDF_Array final |
| 65 | 65 | { |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | - int | |
| 68 | + size_t | |
| 69 | 69 | size() const |
| 70 | 70 | { |
| 71 | - return sp ? sp->size : int(elements.size()); | |
| 71 | + return sp ? sp->size : elements.size(); | |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | 74 | std::unique_ptr<Sparse> sp; | ... | ... |
libtests/sparse_array.cc
| ... | ... | @@ -7,6 +7,12 @@ |
| 7 | 7 | #include <iostream> |
| 8 | 8 | |
| 9 | 9 | int |
| 10 | +to_i(size_t n) | |
| 11 | +{ | |
| 12 | + return static_cast<int>(n); | |
| 13 | +} | |
| 14 | + | |
| 15 | +int | |
| 10 | 16 | main() |
| 11 | 17 | { |
| 12 | 18 | auto obj = QPDFObject::create<QPDF_Array>(std::vector<QPDFObjectHandle>(), true); |
| ... | ... | @@ -65,20 +71,20 @@ main() |
| 65 | 71 | a.setAt(4, QPDFObjectHandle::newNull()); |
| 66 | 72 | assert(a.at(4).second.isNull()); |
| 67 | 73 | |
| 68 | - a.erase(a.size() - 1); | |
| 74 | + a.erase(to_i(a.size()) - 1); | |
| 69 | 75 | assert(a.size() == 5); |
| 70 | 76 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); |
| 71 | 77 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); |
| 72 | 78 | assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third")); |
| 73 | 79 | assert(a.at(4).second.isNull()); |
| 74 | 80 | |
| 75 | - a.erase(a.size() - 1); | |
| 81 | + a.erase(to_i(a.size()) - 1); | |
| 76 | 82 | assert(a.size() == 4); |
| 77 | 83 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); |
| 78 | 84 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); |
| 79 | 85 | assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third")); |
| 80 | 86 | |
| 81 | - a.erase(a.size() - 1); | |
| 87 | + a.erase(to_i(a.size()) - 1); | |
| 82 | 88 | assert(a.size() == 3); |
| 83 | 89 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); |
| 84 | 90 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); | ... | ... |