Commit 38cf7c16283ec2d476514a54e2b1a7016b0b770a
1 parent
de29fd56
Add separate sparse mode to QPDF_Array
Add temporary clone of SparseOHArray to implement non-sparse mode.
Showing
6 changed files
with
331 additions
and
42 deletions
include/qpdf/QPDFObjectHandle.hh
libqpdf/CMakeLists.txt
libqpdf/OHArray.cc
0 → 100644
| 1 | +#include <qpdf/OHArray.hh> | |
| 2 | + | |
| 3 | +#include <qpdf/QPDFObjectHandle.hh> | |
| 4 | +#include <qpdf/QPDFObject_private.hh> | |
| 5 | + | |
| 6 | +#include <stdexcept> | |
| 7 | + | |
| 8 | +OHArray::OHArray() : | |
| 9 | + n_elements(0) | |
| 10 | +{ | |
| 11 | +} | |
| 12 | + | |
| 13 | +size_t | |
| 14 | +OHArray::size() const | |
| 15 | +{ | |
| 16 | + return this->n_elements; | |
| 17 | +} | |
| 18 | + | |
| 19 | +void | |
| 20 | +OHArray::append(QPDFObjectHandle oh) | |
| 21 | +{ | |
| 22 | + if (!oh.isDirectNull()) { | |
| 23 | + this->elements[this->n_elements] = oh; | |
| 24 | + } | |
| 25 | + ++this->n_elements; | |
| 26 | +} | |
| 27 | + | |
| 28 | +void | |
| 29 | +OHArray::append(std::shared_ptr<QPDFObject>&& obj) | |
| 30 | +{ | |
| 31 | + if (obj->getTypeCode() != ::ot_null || !obj->getObjGen().isIndirect()) { | |
| 32 | + this->elements[this->n_elements] = std::move(obj); | |
| 33 | + } | |
| 34 | + ++this->n_elements; | |
| 35 | +} | |
| 36 | + | |
| 37 | +QPDFObjectHandle | |
| 38 | +OHArray::at(size_t idx) const | |
| 39 | +{ | |
| 40 | + if (idx >= this->n_elements) { | |
| 41 | + throw std::logic_error( | |
| 42 | + "INTERNAL ERROR: bounds error accessing OHArray element"); | |
| 43 | + } | |
| 44 | + auto const& iter = this->elements.find(idx); | |
| 45 | + if (iter == this->elements.end()) { | |
| 46 | + return QPDFObjectHandle::newNull(); | |
| 47 | + } else { | |
| 48 | + return (*iter).second; | |
| 49 | + } | |
| 50 | +} | |
| 51 | + | |
| 52 | +void | |
| 53 | +OHArray::remove_last() | |
| 54 | +{ | |
| 55 | + if (this->n_elements == 0) { | |
| 56 | + throw std::logic_error("INTERNAL ERROR: attempt to remove" | |
| 57 | + " last item from empty OHArray"); | |
| 58 | + } | |
| 59 | + --this->n_elements; | |
| 60 | + this->elements.erase(this->n_elements); | |
| 61 | +} | |
| 62 | + | |
| 63 | +void | |
| 64 | +OHArray::disconnect() | |
| 65 | +{ | |
| 66 | + for (auto& iter: this->elements) { | |
| 67 | + QPDFObjectHandle::DisconnectAccess::disconnect(iter.second); | |
| 68 | + } | |
| 69 | +} | |
| 70 | + | |
| 71 | +void | |
| 72 | +OHArray::setAt(size_t idx, QPDFObjectHandle oh) | |
| 73 | +{ | |
| 74 | + if (idx >= this->n_elements) { | |
| 75 | + throw std::logic_error("bounds error setting item in OHArray"); | |
| 76 | + } | |
| 77 | + if (oh.isDirectNull()) { | |
| 78 | + this->elements.erase(idx); | |
| 79 | + } else { | |
| 80 | + this->elements[idx] = oh; | |
| 81 | + } | |
| 82 | +} | |
| 83 | + | |
| 84 | +void | |
| 85 | +OHArray::erase(size_t idx) | |
| 86 | +{ | |
| 87 | + if (idx >= this->n_elements) { | |
| 88 | + throw std::logic_error("bounds error erasing item from OHArray"); | |
| 89 | + } | |
| 90 | + decltype(this->elements) dest; | |
| 91 | + for (auto const& iter: this->elements) { | |
| 92 | + if (iter.first < idx) { | |
| 93 | + dest.insert(iter); | |
| 94 | + } else if (iter.first > idx) { | |
| 95 | + dest[iter.first - 1] = iter.second; | |
| 96 | + } | |
| 97 | + } | |
| 98 | + this->elements = dest; | |
| 99 | + --this->n_elements; | |
| 100 | +} | |
| 101 | + | |
| 102 | +void | |
| 103 | +OHArray::insert(size_t idx, QPDFObjectHandle oh) | |
| 104 | +{ | |
| 105 | + if (idx > this->n_elements) { | |
| 106 | + throw std::logic_error("bounds error inserting item to OHArray"); | |
| 107 | + } else if (idx == this->n_elements) { | |
| 108 | + // Allow inserting to the last position | |
| 109 | + append(oh); | |
| 110 | + } else { | |
| 111 | + decltype(this->elements) dest; | |
| 112 | + for (auto const& iter: this->elements) { | |
| 113 | + if (iter.first < idx) { | |
| 114 | + dest.insert(iter); | |
| 115 | + } else { | |
| 116 | + dest[iter.first + 1] = iter.second; | |
| 117 | + } | |
| 118 | + } | |
| 119 | + this->elements = dest; | |
| 120 | + this->elements[idx] = oh; | |
| 121 | + ++this->n_elements; | |
| 122 | + } | |
| 123 | +} | |
| 124 | + | |
| 125 | +OHArray | |
| 126 | +OHArray::copy() | |
| 127 | +{ | |
| 128 | + OHArray result; | |
| 129 | + result.n_elements = this->n_elements; | |
| 130 | + for (auto const& element: this->elements) { | |
| 131 | + auto value = element.second; | |
| 132 | + result.elements[element.first] = | |
| 133 | + value.isIndirect() ? value : value.shallowCopy(); | |
| 134 | + } | |
| 135 | + return result; | |
| 136 | +} | |
| 137 | + | |
| 138 | +OHArray::const_iterator | |
| 139 | +OHArray::begin() const | |
| 140 | +{ | |
| 141 | + return this->elements.begin(); | |
| 142 | +} | |
| 143 | + | |
| 144 | +OHArray::const_iterator | |
| 145 | +OHArray::end() const | |
| 146 | +{ | |
| 147 | + return this->elements.end(); | |
| 148 | +} | ... | ... |
libqpdf/QPDF_Array.cc
| ... | ... | @@ -19,6 +19,13 @@ QPDF_Array::QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& v) : |
| 19 | 19 | |
| 20 | 20 | QPDF_Array::QPDF_Array(SparseOHArray const& items) : |
| 21 | 21 | QPDFValue(::ot_array, "array"), |
| 22 | + sp_elements(items) | |
| 23 | +{ | |
| 24 | +} | |
| 25 | + | |
| 26 | +QPDF_Array::QPDF_Array(OHArray const& items) : | |
| 27 | + QPDFValue(::ot_array, "array"), | |
| 28 | + sparse(false), | |
| 22 | 29 | elements(items) |
| 23 | 30 | { |
| 24 | 31 | } |
| ... | ... | @@ -42,92 +49,167 @@ QPDF_Array::create(SparseOHArray const& items) |
| 42 | 49 | } |
| 43 | 50 | |
| 44 | 51 | std::shared_ptr<QPDFObject> |
| 52 | +QPDF_Array::create(OHArray const& items) | |
| 53 | +{ | |
| 54 | + return do_create(new QPDF_Array(items)); | |
| 55 | +} | |
| 56 | + | |
| 57 | +std::shared_ptr<QPDFObject> | |
| 45 | 58 | QPDF_Array::copy(bool shallow) |
| 46 | 59 | { |
| 47 | - return create(shallow ? elements : elements.copy()); | |
| 60 | + if (sparse) { | |
| 61 | + return create(shallow ? sp_elements : sp_elements.copy()); | |
| 62 | + } else { | |
| 63 | + return create(shallow ? elements : elements.copy()); | |
| 64 | + } | |
| 48 | 65 | } |
| 49 | 66 | |
| 50 | 67 | void |
| 51 | 68 | QPDF_Array::disconnect() |
| 52 | 69 | { |
| 53 | - elements.disconnect(); | |
| 70 | + if (sparse) { | |
| 71 | + sp_elements.disconnect(); | |
| 72 | + } else { | |
| 73 | + elements.disconnect(); | |
| 74 | + } | |
| 54 | 75 | } |
| 55 | 76 | |
| 56 | 77 | std::string |
| 57 | 78 | QPDF_Array::unparse() |
| 58 | 79 | { |
| 59 | - std::string result = "[ "; | |
| 60 | - size_t size = this->elements.size(); | |
| 61 | - for (size_t i = 0; i < size; ++i) { | |
| 62 | - result += this->elements.at(i).unparse(); | |
| 63 | - result += " "; | |
| 80 | + if (sparse) { | |
| 81 | + std::string result = "[ "; | |
| 82 | + size_t size = sp_elements.size(); | |
| 83 | + for (size_t i = 0; i < size; ++i) { | |
| 84 | + result += sp_elements.at(i).unparse(); | |
| 85 | + result += " "; | |
| 86 | + } | |
| 87 | + result += "]"; | |
| 88 | + return result; | |
| 89 | + } else { | |
| 90 | + std::string result = "[ "; | |
| 91 | + size_t size = elements.size(); | |
| 92 | + for (size_t i = 0; i < size; ++i) { | |
| 93 | + result += elements.at(i).unparse(); | |
| 94 | + result += " "; | |
| 95 | + } | |
| 96 | + result += "]"; | |
| 97 | + return result; | |
| 64 | 98 | } |
| 65 | - result += "]"; | |
| 66 | - return result; | |
| 67 | 99 | } |
| 68 | 100 | |
| 69 | 101 | JSON |
| 70 | 102 | QPDF_Array::getJSON(int json_version) |
| 71 | 103 | { |
| 72 | - JSON j = JSON::makeArray(); | |
| 73 | - size_t size = this->elements.size(); | |
| 74 | - for (size_t i = 0; i < size; ++i) { | |
| 75 | - j.addArrayElement(this->elements.at(i).getJSON(json_version)); | |
| 104 | + if (sparse) { | |
| 105 | + JSON j = JSON::makeArray(); | |
| 106 | + size_t size = sp_elements.size(); | |
| 107 | + for (size_t i = 0; i < size; ++i) { | |
| 108 | + j.addArrayElement(sp_elements.at(i).getJSON(json_version)); | |
| 109 | + } | |
| 110 | + return j; | |
| 111 | + } else { | |
| 112 | + JSON j = JSON::makeArray(); | |
| 113 | + size_t size = elements.size(); | |
| 114 | + for (size_t i = 0; i < size; ++i) { | |
| 115 | + j.addArrayElement(elements.at(i).getJSON(json_version)); | |
| 116 | + } | |
| 117 | + return j; | |
| 76 | 118 | } |
| 77 | - return j; | |
| 78 | 119 | } |
| 79 | 120 | |
| 80 | 121 | int |
| 81 | 122 | QPDF_Array::getNItems() const |
| 82 | 123 | { |
| 83 | - // This should really return a size_t, but changing it would break | |
| 84 | - // a lot of code. | |
| 85 | - return QIntC::to_int(this->elements.size()); | |
| 124 | + if (sparse) { | |
| 125 | + // This should really return a size_t, but changing it would break | |
| 126 | + // a lot of code. | |
| 127 | + return QIntC::to_int(sp_elements.size()); | |
| 128 | + } else { | |
| 129 | + return QIntC::to_int(elements.size()); | |
| 130 | + } | |
| 86 | 131 | } |
| 87 | 132 | |
| 88 | 133 | QPDFObjectHandle |
| 89 | 134 | QPDF_Array::getItem(int n) const |
| 90 | 135 | { |
| 91 | - if ((n < 0) || (n >= QIntC::to_int(elements.size()))) { | |
| 92 | - throw std::logic_error( | |
| 93 | - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 136 | + if (sparse) { | |
| 137 | + if ((n < 0) || (n >= QIntC::to_int(sp_elements.size()))) { | |
| 138 | + throw std::logic_error( | |
| 139 | + "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 140 | + } | |
| 141 | + return sp_elements.at(QIntC::to_size(n)); | |
| 142 | + } else { | |
| 143 | + if ((n < 0) || (n >= QIntC::to_int(elements.size()))) { | |
| 144 | + throw std::logic_error( | |
| 145 | + "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 146 | + } | |
| 147 | + return elements.at(QIntC::to_size(n)); | |
| 94 | 148 | } |
| 95 | - return this->elements.at(QIntC::to_size(n)); | |
| 96 | 149 | } |
| 97 | 150 | |
| 98 | 151 | void |
| 99 | 152 | QPDF_Array::getAsVector(std::vector<QPDFObjectHandle>& v) const |
| 100 | 153 | { |
| 101 | - size_t size = this->elements.size(); | |
| 102 | - for (size_t i = 0; i < size; ++i) { | |
| 103 | - v.push_back(this->elements.at(i)); | |
| 154 | + if (sparse) { | |
| 155 | + size_t size = sp_elements.size(); | |
| 156 | + for (size_t i = 0; i < size; ++i) { | |
| 157 | + v.push_back(sp_elements.at(i)); | |
| 158 | + } | |
| 159 | + } else { | |
| 160 | + size_t size = elements.size(); | |
| 161 | + for (size_t i = 0; i < size; ++i) { | |
| 162 | + v.push_back(elements.at(i)); | |
| 163 | + } | |
| 104 | 164 | } |
| 105 | 165 | } |
| 106 | 166 | |
| 107 | 167 | void |
| 108 | 168 | QPDF_Array::setItem(int n, QPDFObjectHandle const& oh) |
| 109 | 169 | { |
| 110 | - this->elements.setAt(QIntC::to_size(n), oh); | |
| 170 | + if (sparse) { | |
| 171 | + sp_elements.setAt(QIntC::to_size(n), oh); | |
| 172 | + } else { | |
| 173 | + elements.setAt(QIntC::to_size(n), oh); | |
| 174 | + } | |
| 111 | 175 | } |
| 112 | 176 | |
| 113 | 177 | void |
| 114 | 178 | QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v) |
| 115 | 179 | { |
| 116 | - this->elements = SparseOHArray(); | |
| 117 | - for (auto const& iter: v) { | |
| 118 | - this->elements.append(iter); | |
| 180 | + if (sparse) { | |
| 181 | + sp_elements = SparseOHArray(); | |
| 182 | + for (auto const& iter: v) { | |
| 183 | + sp_elements.append(iter); | |
| 184 | + } | |
| 185 | + } else { | |
| 186 | + elements = OHArray(); | |
| 187 | + for (auto const& iter: v) { | |
| 188 | + elements.append(iter); | |
| 189 | + } | |
| 119 | 190 | } |
| 120 | 191 | } |
| 121 | 192 | |
| 122 | 193 | void |
| 123 | 194 | QPDF_Array::setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& v) |
| 124 | 195 | { |
| 125 | - this->elements = SparseOHArray(); | |
| 126 | - for (auto&& item: v) { | |
| 127 | - if (item) { | |
| 128 | - this->elements.append(item); | |
| 129 | - } else { | |
| 130 | - ++this->elements.n_elements; | |
| 196 | + if (sparse) { | |
| 197 | + sp_elements = SparseOHArray(); | |
| 198 | + for (auto&& item: v) { | |
| 199 | + if (item) { | |
| 200 | + sp_elements.append(item); | |
| 201 | + } else { | |
| 202 | + ++sp_elements.n_elements; | |
| 203 | + } | |
| 204 | + } | |
| 205 | + } else { | |
| 206 | + elements = OHArray(); | |
| 207 | + for (auto&& item: v) { | |
| 208 | + if (item) { | |
| 209 | + elements.append(item); | |
| 210 | + } else { | |
| 211 | + ++elements.n_elements; | |
| 212 | + } | |
| 131 | 213 | } |
| 132 | 214 | } |
| 133 | 215 | } |
| ... | ... | @@ -135,22 +217,39 @@ QPDF_Array::setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& v) |
| 135 | 217 | void |
| 136 | 218 | QPDF_Array::insertItem(int at, QPDFObjectHandle const& item) |
| 137 | 219 | { |
| 138 | - // As special case, also allow insert beyond the end | |
| 139 | - if ((at < 0) || (at > QIntC::to_int(this->elements.size()))) { | |
| 140 | - throw std::logic_error( | |
| 141 | - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 220 | + if (sparse) { | |
| 221 | + // As special case, also allow insert beyond the end | |
| 222 | + if ((at < 0) || (at > QIntC::to_int(sp_elements.size()))) { | |
| 223 | + throw std::logic_error( | |
| 224 | + "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 225 | + } | |
| 226 | + sp_elements.insert(QIntC::to_size(at), item); | |
| 227 | + } else { | |
| 228 | + // As special case, also allow insert beyond the end | |
| 229 | + if ((at < 0) || (at > QIntC::to_int(elements.size()))) { | |
| 230 | + throw std::logic_error( | |
| 231 | + "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 232 | + } | |
| 233 | + elements.insert(QIntC::to_size(at), item); | |
| 142 | 234 | } |
| 143 | - this->elements.insert(QIntC::to_size(at), item); | |
| 144 | 235 | } |
| 145 | 236 | |
| 146 | 237 | void |
| 147 | 238 | QPDF_Array::appendItem(QPDFObjectHandle const& item) |
| 148 | 239 | { |
| 149 | - this->elements.append(item); | |
| 240 | + if (sparse) { | |
| 241 | + sp_elements.append(item); | |
| 242 | + } else { | |
| 243 | + elements.append(item); | |
| 244 | + } | |
| 150 | 245 | } |
| 151 | 246 | |
| 152 | 247 | void |
| 153 | 248 | QPDF_Array::eraseItem(int at) |
| 154 | 249 | { |
| 155 | - this->elements.erase(QIntC::to_size(at)); | |
| 250 | + if (sparse) { | |
| 251 | + sp_elements.erase(QIntC::to_size(at)); | |
| 252 | + } else { | |
| 253 | + elements.erase(QIntC::to_size(at)); | |
| 254 | + } | |
| 156 | 255 | } | ... | ... |
libqpdf/qpdf/OHArray.hh
0 → 100644
| 1 | +#ifndef QPDF_OHARRAY_HH | |
| 2 | +#define QPDF_OHARRAY_HH | |
| 3 | + | |
| 4 | +#include <qpdf/QPDFObjectHandle.hh> | |
| 5 | +#include <unordered_map> | |
| 6 | + | |
| 7 | +class QPDF_Array; | |
| 8 | + | |
| 9 | +class OHArray | |
| 10 | +{ | |
| 11 | + public: | |
| 12 | + OHArray(); | |
| 13 | + size_t size() const; | |
| 14 | + void append(QPDFObjectHandle oh); | |
| 15 | + void append(std::shared_ptr<QPDFObject>&& obj); | |
| 16 | + QPDFObjectHandle at(size_t idx) const; | |
| 17 | + void remove_last(); | |
| 18 | + void setAt(size_t idx, QPDFObjectHandle oh); | |
| 19 | + void erase(size_t idx); | |
| 20 | + void insert(size_t idx, QPDFObjectHandle oh); | |
| 21 | + OHArray copy(); | |
| 22 | + void disconnect(); | |
| 23 | + | |
| 24 | + typedef std::unordered_map<size_t, QPDFObjectHandle>::const_iterator | |
| 25 | + const_iterator; | |
| 26 | + const_iterator begin() const; | |
| 27 | + const_iterator end() const; | |
| 28 | + | |
| 29 | + private: | |
| 30 | + friend class QPDF_Array; | |
| 31 | + std::unordered_map<size_t, QPDFObjectHandle> elements; | |
| 32 | + size_t n_elements; | |
| 33 | +}; | |
| 34 | + | |
| 35 | +#endif // QPDF_OHARRAY_HH | ... | ... |
libqpdf/qpdf/QPDF_Array.hh
| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | |
| 4 | 4 | #include <qpdf/QPDFValue.hh> |
| 5 | 5 | |
| 6 | +#include <qpdf/OHArray.hh> | |
| 6 | 7 | #include <qpdf/SparseOHArray.hh> |
| 7 | 8 | #include <list> |
| 8 | 9 | #include <vector> |
| ... | ... | @@ -16,6 +17,7 @@ class QPDF_Array: public QPDFValue |
| 16 | 17 | static std::shared_ptr<QPDFObject> |
| 17 | 18 | create(std::vector<std::shared_ptr<QPDFObject>>&& items); |
| 18 | 19 | static std::shared_ptr<QPDFObject> create(SparseOHArray const& items); |
| 20 | + static std::shared_ptr<QPDFObject> create(OHArray const& items); | |
| 19 | 21 | virtual std::shared_ptr<QPDFObject> copy(bool shallow = false); |
| 20 | 22 | virtual std::string unparse(); |
| 21 | 23 | virtual JSON getJSON(int json_version); |
| ... | ... | @@ -36,7 +38,10 @@ class QPDF_Array: public QPDFValue |
| 36 | 38 | QPDF_Array(std::vector<QPDFObjectHandle> const& items); |
| 37 | 39 | QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items); |
| 38 | 40 | QPDF_Array(SparseOHArray const& items); |
| 39 | - SparseOHArray elements; | |
| 41 | + QPDF_Array(OHArray const& items); | |
| 42 | + bool sparse{false}; | |
| 43 | + SparseOHArray sp_elements; | |
| 44 | + OHArray elements; | |
| 40 | 45 | }; |
| 41 | 46 | |
| 42 | 47 | #endif // QPDF_ARRAY_HH | ... | ... |