Commit 1ec18f20c21aea788e31dfcc5eba68b22048611f

Authored by m-holger
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.
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&amp; 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&lt;QPDFObjectHandle&gt; const&amp; 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&amp; 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));
... ...