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,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&amp; p) const @@ -467,22 +466,21 @@ BaseHandle::write_json(int json_version, JSON::Writer&amp; 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&lt;QPDFObjectHandle&gt; const&amp; v) @@ -210,34 +224,39 @@ Array::setFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; 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&amp; item) @@ -255,10 +274,14 @@ Array::push_back(QPDFObjectHandle const&amp; 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));