Commit 5e9de5cd509310bb5f954bb6c767cfec2b2bbeb7
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.
Showing
2 changed files
with
49 additions
and
47 deletions
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<QPDFObjectHandle> const& 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& 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& 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 | ... | ... |