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,6 +61,11 @@ namespace qpdf | ||
| 61 | 61 | ||
| 62 | // The rest of the header file is for qpdf internal use only. | 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 | std::shared_ptr<QPDFObject> copy(bool shallow = false) const; | 69 | std::shared_ptr<QPDFObject> copy(bool shallow = false) const; |
| 65 | // Recursively remove association with any QPDF object. This method may only be called | 70 | // Recursively remove association with any QPDF object. This method may only be called |
| 66 | // during final destruction. | 71 | // during final destruction. |
libqpdf/QPDFObjectHandle.cc
| @@ -352,16 +352,15 @@ BaseHandle::unparse() const | @@ -352,16 +352,15 @@ BaseHandle::unparse() const | ||
| 352 | auto const& a = std::get<QPDF_Array>(obj->value); | 352 | auto const& a = std::get<QPDF_Array>(obj->value); |
| 353 | std::string result = "[ "; | 353 | std::string result = "[ "; |
| 354 | if (a.sp) { | 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 | result += "null "; | 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 | result += "null "; | 364 | result += "null "; |
| 366 | } | 365 | } |
| 367 | } else { | 366 | } else { |
| @@ -467,22 +466,21 @@ BaseHandle::write_json(int json_version, JSON::Writer& p) const | @@ -467,22 +466,21 @@ BaseHandle::write_json(int json_version, JSON::Writer& p) const | ||
| 467 | auto const& a = std::get<QPDF_Array>(obj->value); | 466 | auto const& a = std::get<QPDF_Array>(obj->value); |
| 468 | p.writeStart('['); | 467 | p.writeStart('['); |
| 469 | if (a.sp) { | 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 | p.writeNext() << "null"; | 472 | p.writeNext() << "null"; |
| 475 | } | 473 | } |
| 476 | p.writeNext(); | 474 | p.writeNext(); |
| 477 | - auto item_og = item.second.getObj()->getObjGen(); | 475 | + auto item_og = value.getObj()->getObjGen(); |
| 478 | if (item_og.isIndirect()) { | 476 | if (item_og.isIndirect()) { |
| 479 | p << "\"" << item_og.unparse(' ') << " R\""; | 477 | p << "\"" << item_og.unparse(' ') << " R\""; |
| 480 | } else { | 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 | p.writeNext() << "null"; | 484 | p.writeNext() << "null"; |
| 487 | } | 485 | } |
| 488 | } else { | 486 | } else { |
| @@ -1234,7 +1232,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( | @@ -1234,7 +1232,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( | ||
| 1234 | all_description = description; | 1232 | all_description = description; |
| 1235 | std::vector<QPDFObjectHandle> result; | 1233 | std::vector<QPDFObjectHandle> result; |
| 1236 | if (auto array = as_array(strict)) { | 1234 | if (auto array = as_array(strict)) { |
| 1237 | - int n_items = array.size(); | 1235 | + int n_items = static_cast<int>(array.size()); |
| 1238 | for (int i = 0; i < n_items; ++i) { | 1236 | for (int i = 0; i < n_items; ++i) { |
| 1239 | QPDFObjectHandle item = array.at(i).second; | 1237 | QPDFObjectHandle item = array.at(i).second; |
| 1240 | if (item.isStream()) { | 1238 | if (item.isStream()) { |
libqpdf/QPDF_Array.cc
| @@ -2,11 +2,25 @@ | @@ -2,11 +2,25 @@ | ||
| 2 | 2 | ||
| 3 | #include <qpdf/QTC.hh> | 3 | #include <qpdf/QTC.hh> |
| 4 | 4 | ||
| 5 | +#include <utility> | ||
| 6 | + | ||
| 5 | using namespace std::literals; | 7 | using namespace std::literals; |
| 6 | using namespace qpdf; | 8 | using namespace qpdf; |
| 7 | 9 | ||
| 8 | static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); | 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 | inline void | 24 | inline void |
| 11 | Array::checkOwnership(QPDFObjectHandle const& item) const | 25 | Array::checkOwnership(QPDFObjectHandle const& item) const |
| 12 | { | 26 | { |
| @@ -142,24 +156,24 @@ Array::null() const | @@ -142,24 +156,24 @@ Array::null() const | ||
| 142 | return null_oh; | 156 | return null_oh; |
| 143 | } | 157 | } |
| 144 | 158 | ||
| 145 | -int | 159 | +size_t |
| 146 | Array::size() const | 160 | Array::size() const |
| 147 | { | 161 | { |
| 148 | auto a = array(); | 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 | std::pair<bool, QPDFObjectHandle> | 166 | std::pair<bool, QPDFObjectHandle> |
| 153 | Array::at(int n) const | 167 | Array::at(int n) const |
| 154 | { | 168 | { |
| 155 | auto a = array(); | 169 | auto a = array(); |
| 156 | - if (n < 0 || n >= size()) { | 170 | + if (n < 0 || std::cmp_greater_equal(n, size())) { |
| 157 | return {false, {}}; | 171 | return {false, {}}; |
| 158 | } | 172 | } |
| 159 | if (!a->sp) { | 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 | return {true, iter == a->sp->elements.end() ? null() : iter->second}; | 177 | return {true, iter == a->sp->elements.end() ? null() : iter->second}; |
| 164 | } | 178 | } |
| 165 | 179 | ||
| @@ -169,12 +183,12 @@ Array::getAsVector() const | @@ -169,12 +183,12 @@ Array::getAsVector() const | ||
| 169 | auto a = array(); | 183 | auto a = array(); |
| 170 | if (a->sp) { | 184 | if (a->sp) { |
| 171 | std::vector<QPDFObjectHandle> v; | 185 | std::vector<QPDFObjectHandle> v; |
| 172 | - v.reserve(size_t(size())); | 186 | + v.reserve(size()); |
| 173 | for (auto const& item: a->sp->elements) { | 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 | v.emplace_back(item.second); | 189 | v.emplace_back(item.second); |
| 176 | } | 190 | } |
| 177 | - v.resize(size_t(size()), null_oh); | 191 | + v.resize(size(), null_oh); |
| 178 | return v; | 192 | return v; |
| 179 | } else { | 193 | } else { |
| 180 | return a->elements; | 194 | return a->elements; |
| @@ -184,15 +198,15 @@ Array::getAsVector() const | @@ -184,15 +198,15 @@ Array::getAsVector() const | ||
| 184 | bool | 198 | bool |
| 185 | Array::setAt(int at, QPDFObjectHandle const& oh) | 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 | return false; | 202 | return false; |
| 189 | } | 203 | } |
| 190 | auto a = array(); | 204 | auto a = array(); |
| 191 | checkOwnership(oh); | 205 | checkOwnership(oh); |
| 192 | if (a->sp) { | 206 | if (a->sp) { |
| 193 | - a->sp->elements[at] = oh; | 207 | + a->sp->elements[to_s(at)] = oh; |
| 194 | } else { | 208 | } else { |
| 195 | - a->elements[size_t(at)] = oh; | 209 | + a->elements[to_s(at)] = oh; |
| 196 | } | 210 | } |
| 197 | return true; | 211 | return true; |
| 198 | } | 212 | } |
| @@ -210,34 +224,39 @@ Array::setFromVector(std::vector<QPDFObjectHandle> const& v) | @@ -210,34 +224,39 @@ Array::setFromVector(std::vector<QPDFObjectHandle> const& v) | ||
| 210 | } | 224 | } |
| 211 | 225 | ||
| 212 | bool | 226 | bool |
| 213 | -Array::insert(int at, QPDFObjectHandle const& item) | 227 | +Array::insert(int at_i, QPDFObjectHandle const& item) |
| 214 | { | 228 | { |
| 215 | auto a = array(); | 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 | return false; | 236 | return false; |
| 220 | - } else if (at == sz) { | 237 | + } |
| 238 | + if (at == sz) { | ||
| 239 | + // As special case, also allow insert beyond the end | ||
| 221 | push_back(item); | 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 | return true; | 261 | return true; |
| 243 | } | 262 | } |
| @@ -255,10 +274,14 @@ Array::push_back(QPDFObjectHandle const& item) | @@ -255,10 +274,14 @@ Array::push_back(QPDFObjectHandle const& item) | ||
| 255 | } | 274 | } |
| 256 | 275 | ||
| 257 | bool | 276 | bool |
| 258 | -Array::erase(int at) | 277 | +Array::erase(int at_i) |
| 259 | { | 278 | { |
| 260 | auto a = array(); | 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 | return false; | 285 | return false; |
| 263 | } | 286 | } |
| 264 | if (a->sp) { | 287 | if (a->sp) { |
| @@ -277,7 +300,7 @@ Array::erase(int at) | @@ -277,7 +300,7 @@ Array::erase(int at) | ||
| 277 | } | 300 | } |
| 278 | --(a->sp->size); | 301 | --(a->sp->size); |
| 279 | } else { | 302 | } else { |
| 280 | - a->elements.erase(a->elements.cbegin() + at); | 303 | + a->elements.erase(a->elements.cbegin() + at_i); |
| 281 | } | 304 | } |
| 282 | return true; | 305 | return true; |
| 283 | } | 306 | } |
| @@ -286,7 +309,7 @@ int | @@ -286,7 +309,7 @@ int | ||
| 286 | QPDFObjectHandle::getArrayNItems() const | 309 | QPDFObjectHandle::getArrayNItems() const |
| 287 | { | 310 | { |
| 288 | if (auto array = as_array(strict)) { | 311 | if (auto array = as_array(strict)) { |
| 289 | - return array.size(); | 312 | + return to_i(array.size()); |
| 290 | } | 313 | } |
| 291 | typeWarning("array", "treating as empty"); | 314 | typeWarning("array", "treating as empty"); |
| 292 | QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); | 315 | QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); |
| @@ -471,7 +494,37 @@ QPDFObjectHandle | @@ -471,7 +494,37 @@ QPDFObjectHandle | ||
| 471 | QPDFObjectHandle::eraseItemAndGetOld(int at) | 494 | QPDFObjectHandle::eraseItemAndGetOld(int at) |
| 472 | { | 495 | { |
| 473 | auto array = as_array(strict); | 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 | eraseItem(at); | 499 | eraseItem(at); |
| 476 | return result; | 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,7 +38,7 @@ namespace qpdf | ||
| 38 | 38 | ||
| 39 | const_reverse_iterator crend(); | 39 | const_reverse_iterator crend(); |
| 40 | 40 | ||
| 41 | - int size() const; | 41 | + size_t size() const; |
| 42 | std::pair<bool, QPDFObjectHandle> at(int n) const; | 42 | std::pair<bool, QPDFObjectHandle> at(int n) const; |
| 43 | bool setAt(int at, QPDFObjectHandle const& oh); | 43 | bool setAt(int at, QPDFObjectHandle const& oh); |
| 44 | bool insert(int at, QPDFObjectHandle const& item); | 44 | bool insert(int at, QPDFObjectHandle const& item); |
libqpdf/qpdf/QPDFObject_private.hh
| @@ -35,8 +35,8 @@ class QPDF_Array final | @@ -35,8 +35,8 @@ class QPDF_Array final | ||
| 35 | private: | 35 | private: |
| 36 | struct Sparse | 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 | public: | 42 | public: |
| @@ -65,10 +65,10 @@ class QPDF_Array final | @@ -65,10 +65,10 @@ class QPDF_Array final | ||
| 65 | { | 65 | { |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | - int | 68 | + size_t |
| 69 | size() const | 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 | std::unique_ptr<Sparse> sp; | 74 | std::unique_ptr<Sparse> sp; |
libtests/sparse_array.cc
| @@ -7,6 +7,12 @@ | @@ -7,6 +7,12 @@ | ||
| 7 | #include <iostream> | 7 | #include <iostream> |
| 8 | 8 | ||
| 9 | int | 9 | int |
| 10 | +to_i(size_t n) | ||
| 11 | +{ | ||
| 12 | + return static_cast<int>(n); | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +int | ||
| 10 | main() | 16 | main() |
| 11 | { | 17 | { |
| 12 | auto obj = QPDFObject::create<QPDF_Array>(std::vector<QPDFObjectHandle>(), true); | 18 | auto obj = QPDFObject::create<QPDF_Array>(std::vector<QPDFObjectHandle>(), true); |
| @@ -65,20 +71,20 @@ main() | @@ -65,20 +71,20 @@ main() | ||
| 65 | a.setAt(4, QPDFObjectHandle::newNull()); | 71 | a.setAt(4, QPDFObjectHandle::newNull()); |
| 66 | assert(a.at(4).second.isNull()); | 72 | assert(a.at(4).second.isNull()); |
| 67 | 73 | ||
| 68 | - a.erase(a.size() - 1); | 74 | + a.erase(to_i(a.size()) - 1); |
| 69 | assert(a.size() == 5); | 75 | assert(a.size() == 5); |
| 70 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); | 76 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); |
| 71 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); | 77 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); |
| 72 | assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third")); | 78 | assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third")); |
| 73 | assert(a.at(4).second.isNull()); | 79 | assert(a.at(4).second.isNull()); |
| 74 | 80 | ||
| 75 | - a.erase(a.size() - 1); | 81 | + a.erase(to_i(a.size()) - 1); |
| 76 | assert(a.size() == 4); | 82 | assert(a.size() == 4); |
| 77 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); | 83 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); |
| 78 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); | 84 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); |
| 79 | assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third")); | 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 | assert(a.size() == 3); | 88 | assert(a.size() == 3); |
| 83 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); | 89 | assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); |
| 84 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); | 90 | assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); |