Commit 5e9de5cd509310bb5f954bb6c767cfec2b2bbeb7

Authored by m-holger
1 parent 452e1f5c

Tune handling of sparse arrays in QPDF_Array

Sparse arrays are rare. Dynamically create the variables needed to manage
them only when needed.
libqpdf/QPDF_Array.cc
... ... @@ -30,10 +30,7 @@ QPDF_Array::QPDF_Array() :
30 30  
31 31 QPDF_Array::QPDF_Array(QPDF_Array const& other) :
32 32 QPDFValue(::ot_array, "array"),
33   - sparse(other.sparse),
34   - sp_size(other.sp_size),
35   - sp_elements(other.sp_elements),
36   - elements(other.elements)
  33 + sp(other.sp ? std::make_unique<Sparse>(*other.sp) : nullptr)
37 34 {
38 35 }
39 36  
... ... @@ -44,15 +41,15 @@ QPDF_Array::QPDF_Array(std::vector&lt;QPDFObjectHandle&gt; const&amp; v) :
44 41 }
45 42  
46 43 QPDF_Array::QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& v, bool sparse) :
47   - QPDFValue(::ot_array, "array"),
48   - sparse(sparse)
  44 + QPDFValue(::ot_array, "array")
49 45 {
50 46 if (sparse) {
  47 + sp = std::make_unique<Sparse>();
51 48 for (auto&& item: v) {
52 49 if (item->getTypeCode() != ::ot_null || item->getObjGen().isIndirect()) {
53   - sp_elements[sp_size] = std::move(item);
  50 + sp->elements[sp->size] = std::move(item);
54 51 }
55   - ++sp_size;
  52 + ++sp->size;
56 53 }
57 54 } else {
58 55 elements = std::move(v);
... ... @@ -77,12 +74,12 @@ QPDF_Array::copy(bool shallow)
77 74 if (shallow) {
78 75 return do_create(new QPDF_Array(*this));
79 76 } else {
80   - if (sparse) {
  77 + if (sp) {
81 78 auto* result = new QPDF_Array();
82   - result->sp_size = sp_size;
83   - for (auto const& element: sp_elements) {
  79 + result->sp->size = sp->size;
  80 + for (auto const& element: sp->elements) {
84 81 auto const& obj = element.second;
85   - result->sp_elements[element.first] =
  82 + result->sp->elements[element.first] =
86 83 obj->getObjGen().isIndirect() ? obj : obj->copy();
87 84 }
88 85 return do_create(result);
... ... @@ -102,8 +99,8 @@ QPDF_Array::copy(bool shallow)
102 99 void
103 100 QPDF_Array::disconnect()
104 101 {
105   - if (sparse) {
106   - for (auto& item: sp_elements) {
  102 + if (sp) {
  103 + for (auto& item: sp->elements) {
107 104 auto& obj = item.second;
108 105 if (!obj->getObjGen().isIndirect()) {
109 106 obj->disconnect();
... ... @@ -122,9 +119,9 @@ std::string
122 119 QPDF_Array::unparse()
123 120 {
124 121 std::string result = "[ ";
125   - if (sparse) {
  122 + if (sp) {
126 123 int next = 0;
127   - for (auto& item: sp_elements) {
  124 + for (auto& item: sp->elements) {
128 125 int key = item.first;
129 126 for (int j = next; j < key; ++j) {
130 127 result += "null ";
... ... @@ -134,7 +131,7 @@ QPDF_Array::unparse()
134 131 result += og.isIndirect() ? og.unparse(' ') + " R " : item.second->unparse() + " ";
135 132 next = ++key;
136 133 }
137   - for (int j = next; j < sp_size; ++j) {
  134 + for (int j = next; j < sp->size; ++j) {
138 135 result += "null ";
139 136 }
140 137 } else {
... ... @@ -153,9 +150,9 @@ QPDF_Array::getJSON(int json_version)
153 150 {
154 151 static const JSON j_null = JSON::makeNull();
155 152 JSON j_array = JSON::makeArray();
156   - if (sparse) {
  153 + if (sp) {
157 154 int next = 0;
158   - for (auto& item: sp_elements) {
  155 + for (auto& item: sp->elements) {
159 156 int key = item.first;
160 157 for (int j = next; j < key; ++j) {
161 158 j_array.addArrayElement(j_null);
... ... @@ -166,7 +163,7 @@ QPDF_Array::getJSON(int json_version)
166 163 : item.second->getJSON(json_version));
167 164 next = ++key;
168 165 }
169   - for (int j = next; j < sp_size; ++j) {
  166 + for (int j = next; j < sp->size; ++j) {
170 167 j_array.addArrayElement(j_null);
171 168 }
172 169 } else {
... ... @@ -185,9 +182,9 @@ QPDF_Array::at(int n) const noexcept
185 182 {
186 183 if (n < 0 || n >= size()) {
187 184 return {};
188   - } else if (sparse) {
189   - auto const& iter = sp_elements.find(n);
190   - return iter == sp_elements.end() ? null_oh : (*iter).second;
  185 + } else if (sp) {
  186 + auto const& iter = sp->elements.find(n);
  187 + return iter == sp->elements.end() ? null_oh : (*iter).second;
191 188 } else {
192 189 return elements[size_t(n)];
193 190 }
... ... @@ -196,10 +193,10 @@ QPDF_Array::at(int n) const noexcept
196 193 std::vector<QPDFObjectHandle>
197 194 QPDF_Array::getAsVector() const
198 195 {
199   - if (sparse) {
  196 + if (sp) {
200 197 std::vector<QPDFObjectHandle> v;
201 198 v.reserve(size_t(size()));
202   - for (auto const& item: sp_elements) {
  199 + for (auto const& item: sp->elements) {
203 200 v.resize(size_t(item.first), null_oh);
204 201 v.emplace_back(item.second);
205 202 }
... ... @@ -217,8 +214,8 @@ QPDF_Array::setAt(int at, QPDFObjectHandle const&amp; oh)
217 214 return false;
218 215 }
219 216 checkOwnership(oh);
220   - if (sparse) {
221   - sp_elements[at] = oh.getObj();
  217 + if (sp) {
  218 + sp->elements[at] = oh.getObj();
222 219 } else {
223 220 elements[size_t(at)] = oh.getObj();
224 221 }
... ... @@ -247,20 +244,20 @@ QPDF_Array::insert(int at, QPDFObjectHandle const&amp; item)
247 244 push_back(item);
248 245 } else {
249 246 checkOwnership(item);
250   - if (sparse) {
251   - auto iter = sp_elements.crbegin();
252   - while (iter != sp_elements.crend()) {
  247 + if (sp) {
  248 + auto iter = sp->elements.crbegin();
  249 + while (iter != sp->elements.crend()) {
253 250 auto key = (iter++)->first;
254 251 if (key >= at) {
255   - auto nh = sp_elements.extract(key);
  252 + auto nh = sp->elements.extract(key);
256 253 ++nh.key();
257   - sp_elements.insert(std::move(nh));
  254 + sp->elements.insert(std::move(nh));
258 255 } else {
259 256 break;
260 257 }
261 258 }
262   - sp_elements[at] = item.getObj();
263   - ++sp_size;
  259 + sp->elements[at] = item.getObj();
  260 + ++sp->size;
264 261 } else {
265 262 elements.insert(elements.cbegin() + at, item.getObj());
266 263 }
... ... @@ -272,8 +269,8 @@ void
272 269 QPDF_Array::push_back(QPDFObjectHandle const& item)
273 270 {
274 271 checkOwnership(item);
275   - if (sparse) {
276   - sp_elements[sp_size++] = item.getObj();
  272 + if (sp) {
  273 + sp->elements[(sp->size)++] = item.getObj();
277 274 } else {
278 275 elements.push_back(item.getObj());
279 276 }
... ... @@ -285,21 +282,21 @@ QPDF_Array::erase(int at)
285 282 if (at < 0 || at >= size()) {
286 283 return false;
287 284 }
288   - if (sparse) {
289   - auto end = sp_elements.end();
290   - if (auto iter = sp_elements.lower_bound(at); iter != end) {
  285 + if (sp) {
  286 + auto end = sp->elements.end();
  287 + if (auto iter = sp->elements.lower_bound(at); iter != end) {
291 288 if (iter->first == at) {
292 289 iter++;
293   - sp_elements.erase(at);
  290 + sp->elements.erase(at);
294 291 }
295 292  
296 293 while (iter != end) {
297   - auto nh = sp_elements.extract(iter++);
  294 + auto nh = sp->elements.extract(iter++);
298 295 --nh.key();
299   - sp_elements.insert(std::move(nh));
  296 + sp->elements.insert(std::move(nh));
300 297 }
301 298 }
302   - --sp_size;
  299 + --(sp->size);
303 300 } else {
304 301 elements.erase(elements.cbegin() + at);
305 302 }
... ...
libqpdf/qpdf/QPDF_Array.hh
... ... @@ -8,6 +8,13 @@
8 8  
9 9 class QPDF_Array: public QPDFValue
10 10 {
  11 + private:
  12 + struct Sparse
  13 + {
  14 + int size{0};
  15 + std::map<int, std::shared_ptr<QPDFObject>> elements;
  16 + };
  17 +
11 18 public:
12 19 ~QPDF_Array() override = default;
13 20 static std::shared_ptr<QPDFObject> create(std::vector<QPDFObjectHandle> const& items);
... ... @@ -21,7 +28,7 @@ class QPDF_Array: public QPDFValue
21 28 int
22 29 size() const noexcept
23 30 {
24   - return sparse ? sp_size : int(elements.size());
  31 + return sp ? sp->size : int(elements.size());
25 32 }
26 33 QPDFObjectHandle at(int n) const noexcept;
27 34 bool setAt(int n, QPDFObjectHandle const& oh);
... ... @@ -39,9 +46,7 @@ class QPDF_Array: public QPDFValue
39 46  
40 47 void checkOwnership(QPDFObjectHandle const& item) const;
41 48  
42   - bool sparse{false};
43   - int sp_size{0};
44   - std::map<int, std::shared_ptr<QPDFObject>> sp_elements;
  49 + std::unique_ptr<Sparse> sp;
45 50 std::vector<std::shared_ptr<QPDFObject>> elements;
46 51 };
47 52  
... ...