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