diff options
Diffstat (limited to 'libqpdf')
-rw-r--r-- | libqpdf/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libqpdf/QPDFObjectHandle.cc | 178 | ||||
-rw-r--r-- | libqpdf/QPDFParser.cc | 26 | ||||
-rw-r--r-- | libqpdf/QPDF_Array.cc | 312 | ||||
-rw-r--r-- | libqpdf/QPDF_Null.cc | 1 | ||||
-rw-r--r-- | libqpdf/SparseOHArray.cc | 148 | ||||
-rw-r--r-- | libqpdf/qpdf/QPDF_Array.hh | 44 | ||||
-rw-r--r-- | libqpdf/qpdf/SparseOHArray.hh | 35 |
8 files changed, 353 insertions, 392 deletions
diff --git a/libqpdf/CMakeLists.txt b/libqpdf/CMakeLists.txt index 5e3a628e..623e05d3 100644 --- a/libqpdf/CMakeLists.txt +++ b/libqpdf/CMakeLists.txt @@ -115,7 +115,6 @@ set(libqpdf_SOURCES ResourceFinder.cc SecureRandomDataProvider.cc SF_FlateLzwDecode.cc - SparseOHArray.cc qpdf-c.cc qpdfjob-c.cc qpdflogger-c.cc) diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index d474dcce..b3f208a5 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -23,7 +23,6 @@ #include <qpdf/QPDF_Stream.hh> #include <qpdf/QPDF_String.hh> #include <qpdf/QPDF_Unresolved.hh> -#include <qpdf/SparseOHArray.hh> #include <qpdf/QIntC.hh> #include <qpdf/QTC.hh> @@ -789,9 +788,8 @@ QPDFObjectHandle::aitems() int QPDFObjectHandle::getArrayNItems() { - auto array = asArray(); - if (array) { - return array->getNItems(); + if (auto array = asArray()) { + return array->size(); } else { typeWarning("array", "treating as empty"); QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); @@ -802,104 +800,101 @@ QPDFObjectHandle::getArrayNItems() QPDFObjectHandle QPDFObjectHandle::getArrayItem(int n) { - auto array = asArray(); - if (array && (n < array->getNItems()) && (n >= 0)) { - return array->getItem(n); - } else { - if (array) { + if (auto array = asArray()) { + if (auto result = array->at(n); result.obj != nullptr) { + return result; + } else { objectWarning("returning null for out of bounds array access"); QTC::TC("qpdf", "QPDFObjectHandle array bounds"); - } else { - typeWarning("array", "returning null"); - QTC::TC("qpdf", "QPDFObjectHandle array null for non-array"); } - static auto constexpr msg = - " -> null returned from invalid array access"sv; - return QPDF_Null::create(obj, msg, ""); + } else { + typeWarning("array", "returning null"); + QTC::TC("qpdf", "QPDFObjectHandle array null for non-array"); } + static auto constexpr msg = " -> null returned from invalid array access"sv; + return QPDF_Null::create(obj, msg, ""); } bool QPDFObjectHandle::isRectangle() { - auto array = asArray(); - if ((array == nullptr) || (array->getNItems() != 4)) { - return false; - } - for (int i = 0; i < 4; ++i) { - if (!array->getItem(i).isNumber()) { - return false; + if (auto array = asArray()) { + for (int i = 0; i < 4; ++i) { + if (auto item = array->at(i); !(item.obj && item.isNumber())) { + return false; + } } + return array->size() == 4; } - return true; + return false; } bool QPDFObjectHandle::isMatrix() { - auto array = asArray(); - if ((array == nullptr) || (array->getNItems() != 6)) { - return false; - } - for (int i = 0; i < 6; ++i) { - if (!array->getItem(i).isNumber()) { - return false; + if (auto array = asArray()) { + for (int i = 0; i < 6; ++i) { + if (auto item = array->at(i); !(item.obj && item.isNumber())) { + return false; + } } + return array->size() == 6; } - return true; + return false; } QPDFObjectHandle::Rectangle QPDFObjectHandle::getArrayAsRectangle() { - Rectangle result; - if (isRectangle()) { - auto array = asArray(); - // Rectangle coordinates are always supposed to be llx, lly, - // urx, ury, but files have been found in the wild where - // llx > urx or lly > ury. - double i0 = array->getItem(0).getNumericValue(); - double i1 = array->getItem(1).getNumericValue(); - double i2 = array->getItem(2).getNumericValue(); - double i3 = array->getItem(3).getNumericValue(); - result = Rectangle( - std::min(i0, i2), - std::min(i1, i3), - std::max(i0, i2), - std::max(i1, i3)); + if (auto array = asArray()) { + if (array->size() != 4) { + return {}; + } + double items[4]; + for (int i = 0; i < 4; ++i) { + if (!array->at(i).getValueAsNumber(items[i])) { + return {}; + } + } + return Rectangle( + std::min(items[0], items[2]), + std::min(items[1], items[3]), + std::max(items[0], items[2]), + std::max(items[1], items[3])); } - return result; + return {}; } QPDFObjectHandle::Matrix QPDFObjectHandle::getArrayAsMatrix() { - Matrix result; - if (isMatrix()) { - auto array = asArray(); - result = Matrix( - array->getItem(0).getNumericValue(), - array->getItem(1).getNumericValue(), - array->getItem(2).getNumericValue(), - array->getItem(3).getNumericValue(), - array->getItem(4).getNumericValue(), - array->getItem(5).getNumericValue()); + if (auto array = asArray()) { + if (array->size() != 6) { + return {}; + } + double items[6]; + for (int i = 0; i < 6; ++i) { + if (!array->at(i).getValueAsNumber(items[i])) { + return {}; + } + } + return Matrix( + items[0], items[1], items[2], items[3], items[4], items[5]); } - return result; + return {}; } std::vector<QPDFObjectHandle> QPDFObjectHandle::getArrayAsVector() { - std::vector<QPDFObjectHandle> result; auto array = asArray(); if (array) { - array->getAsVector(result); + return array->getAsVector(); } else { typeWarning("array", "treating as empty"); QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); } - return result; + return {}; } // Array mutators @@ -907,24 +902,20 @@ QPDFObjectHandle::getArrayAsVector() void QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) { - auto array = asArray(); - if (array) { - checkOwnership(item); - array->setItem(n, item); + if (auto array = asArray()) { + if (!array->setAt(n, item)) { + objectWarning("ignoring attempt to set out of bounds array item"); + QTC::TC("qpdf", "QPDFObjectHandle set array bounds"); + } } else { typeWarning("array", "ignoring attempt to set item"); QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item"); } } - void QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) { - auto array = asArray(); - if (array) { - for (auto const& item: items) { - checkOwnership(item); - } + if (auto array = asArray()) { array->setFromVector(items); } else { typeWarning("array", "ignoring attempt to replace items"); @@ -935,9 +926,12 @@ QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) void QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) { - auto array = asArray(); - if (array) { - array->insertItem(at, item); + if (auto array = asArray()) { + if (!array->insert(at, item)) { + objectWarning( + "ignoring attempt to insert out of bounds array item"); + QTC::TC("qpdf", "QPDFObjectHandle insert array bounds"); + } } else { typeWarning("array", "ignoring attempt to insert item"); QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item"); @@ -954,10 +948,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item) void QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) { - auto array = asArray(); - if (array) { - checkOwnership(item); - array->appendItem(item); + if (auto array = asArray()) { + array->push_back(item); } else { typeWarning("array", "ignoring attempt to append item"); QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); @@ -974,28 +966,23 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item) void QPDFObjectHandle::eraseItem(int at) { - auto array = asArray(); - if (array && (at < array->getNItems()) && (at >= 0)) { - array->eraseItem(at); - } else { - if (array) { + if (auto array = asArray()) { + if (!array->erase(at)) { objectWarning("ignoring attempt to erase out of bounds array item"); QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); - } else { - typeWarning("array", "ignoring attempt to erase item"); - QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item"); } + } else { + typeWarning("array", "ignoring attempt to erase item"); + QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item"); } } QPDFObjectHandle QPDFObjectHandle::eraseItemAndGetOld(int at) { - auto result = QPDFObjectHandle::newNull(); auto array = asArray(); - if (array && (at < array->getNItems()) && (at >= 0)) { - result = array->getItem(at); - } + auto result = + (array && at < array->size() && at >= 0) ? array->at(at) : newNull(); eraseItem(at); return result; } @@ -1515,11 +1502,10 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( { all_description = description; std::vector<QPDFObjectHandle> result; - auto array = asArray(); - if (array) { - int n_items = array->getNItems(); + if (auto array = asArray()) { + int n_items = array->size(); for (int i = 0; i < n_items; ++i) { - QPDFObjectHandle item = array->getItem(i); + QPDFObjectHandle item = array->at(i); if (item.isStream()) { result.push_back(item); } else { @@ -2217,9 +2203,9 @@ QPDFObjectHandle::makeDirect( } else if (isArray()) { std::vector<QPDFObjectHandle> items; auto array = asArray(); - int n = array->getNItems(); + int n = array->size(); for (int i = 0; i < n; ++i) { - items.push_back(array->getItem(i)); + items.push_back(array->at(i)); items.back().makeDirect(visited, stop_at_streams); } this->obj = QPDF_Array::create(items); diff --git a/libqpdf/QPDFParser.cc b/libqpdf/QPDFParser.cc index 09bf1601..4c43e487 100644 --- a/libqpdf/QPDFParser.cc +++ b/libqpdf/QPDFParser.cc @@ -27,16 +27,15 @@ namespace struct StackFrame { StackFrame(std::shared_ptr<InputSource> input) : - offset(input->tell()), - contents_string(""), - contents_offset(-1) + offset(input->tell()) { } std::vector<std::shared_ptr<QPDFObject>> olist; qpdf_offset_t offset; - std::string contents_string; - qpdf_offset_t contents_offset; + std::string contents_string{""}; + qpdf_offset_t contents_offset{-1}; + int null_count{0}; }; } // namespace @@ -50,6 +49,7 @@ QPDFParser::parse(bool& empty, bool content_stream) // this, it will cause a logic error to be thrown from // QPDF::inParse(). + const static std::shared_ptr<QPDFObject> null_oh = QPDF_Null::create(); QPDF::ParseGuard pg(context); empty = false; @@ -67,7 +67,6 @@ QPDFParser::parse(bool& empty, bool content_stream) int good_count = 0; bool b_contents = false; bool is_null = false; - auto null_oh = QPDF_Null::create(); while (!done) { bool bad = false; @@ -156,6 +155,8 @@ QPDFParser::parse(bool& empty, bool content_stream) case QPDFTokenizer::tt_null: is_null = true; + ++frame.null_count; + break; case QPDFTokenizer::tt_integer: @@ -301,9 +302,11 @@ QPDFParser::parse(bool& empty, bool content_stream) case st_dictionary: case st_array: - if (!indirect_ref && !is_null) { - // No need to set description for direct nulls - they will - // become implicit. + if (is_null) { + object = null_oh; + // No need to set description for direct nulls - they probably + // will become implicit. + } else if (!indirect_ref) { setDescription(object, input->getLastOffset()); } set_offset = true; @@ -326,7 +329,8 @@ QPDFParser::parse(bool& empty, bool content_stream) parser_state_e old_state = state_stack.back(); state_stack.pop_back(); if (old_state == st_array) { - object = QPDF_Array::create(std::move(olist)); + object = QPDF_Array::create( + std::move(olist), frame.null_count > 100); setDescription(object, offset - 1); // The `offset` points to the next of "[". Set the rewind // offset to point to the beginning of "[". This has been @@ -381,7 +385,7 @@ QPDFParser::parse(bool& empty, bool content_stream) // Calculate value. std::shared_ptr<QPDFObject> val; if (iter != olist.end()) { - val = *iter ? *iter : QPDF_Null::create(); + val = *iter; ++iter; } else { QTC::TC("qpdf", "QPDFParser no val for last key"); diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc index de34103e..3bd139b1 100644 --- a/libqpdf/QPDF_Array.cc +++ b/libqpdf/QPDF_Array.cc @@ -1,66 +1,157 @@ #include <qpdf/QPDF_Array.hh> -#include <qpdf/QIntC.hh> +#include <qpdf/QPDFObjectHandle.hh> #include <qpdf/QPDFObject_private.hh> -#include <qpdf/QUtil.hh> -#include <stdexcept> -QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) : - QPDFValue(::ot_array, "array") +static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); + +inline void +QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const { - setFromVector(v); + if (auto obj = item.getObjectPtr()) { + if (qpdf) { + if (auto item_qpdf = obj->getQPDF()) { + if (qpdf != item_qpdf) { + throw std::logic_error( + "Attempting to add an object from a different QPDF. " + "Use QPDF::copyForeignObject to add objects from " + "another file."); + } + } + } + } else { + throw std::logic_error( + "Attempting to add an uninitialized object to a QPDF_Array."); + } } -QPDF_Array::QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& v) : +QPDF_Array::QPDF_Array() : QPDFValue(::ot_array, "array") { - setFromVector(std::move(v)); } -QPDF_Array::QPDF_Array(SparseOHArray const& items) : +QPDF_Array::QPDF_Array(QPDF_Array const& other) : QPDFValue(::ot_array, "array"), - elements(items) + sparse(other.sparse), + sp_size(other.sp_size), + sp_elements(other.sp_elements), + elements(other.elements) { } -std::shared_ptr<QPDFObject> -QPDF_Array::create(std::vector<QPDFObjectHandle> const& items) +QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) : + QPDFValue(::ot_array, "array") { - return do_create(new QPDF_Array(items)); + setFromVector(v); } -std::shared_ptr<QPDFObject> -QPDF_Array::create(std::vector<std::shared_ptr<QPDFObject>>&& items) +QPDF_Array::QPDF_Array( + std::vector<std::shared_ptr<QPDFObject>>&& v, bool sparse) : + QPDFValue(::ot_array, "array"), + sparse(sparse) { - return do_create(new QPDF_Array(std::move(items))); + if (sparse) { + for (auto&& item: v) { + if (item->getTypeCode() != ::ot_null || + item->getObjGen().isIndirect()) { + sp_elements[sp_size] = std::move(item); + } + ++sp_size; + } + } else { + elements = std::move(v); + } } std::shared_ptr<QPDFObject> -QPDF_Array::create(SparseOHArray const& items) +QPDF_Array::create(std::vector<QPDFObjectHandle> const& items) { return do_create(new QPDF_Array(items)); } std::shared_ptr<QPDFObject> +QPDF_Array::create( + std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse) +{ + return do_create(new QPDF_Array(std::move(items), sparse)); +} + +std::shared_ptr<QPDFObject> QPDF_Array::copy(bool shallow) { - return create(shallow ? elements : elements.copy()); + if (shallow) { + return do_create(new QPDF_Array(*this)); + } else { + if (sparse) { + QPDF_Array* result = new QPDF_Array(); + result->sp_size = sp_size; + for (auto const& element: sp_elements) { + auto const& obj = element.second; + result->sp_elements[element.first] = + obj->getObjGen().isIndirect() ? obj : obj->copy(); + } + return do_create(result); + } else { + std::vector<std::shared_ptr<QPDFObject>> result; + result.reserve(elements.size()); + for (auto const& element: elements) { + result.push_back( + element + ? (element->getObjGen().isIndirect() ? element + : element->copy()) + : element); + } + return create(std::move(result), false); + } + } } void QPDF_Array::disconnect() { - elements.disconnect(); + if (sparse) { + for (auto& item: sp_elements) { + auto& obj = item.second; + if (!obj->getObjGen().isIndirect()) { + obj->disconnect(); + } + } + } else { + for (auto& obj: elements) { + if (!obj->getObjGen().isIndirect()) { + obj->disconnect(); + } + } + } } std::string QPDF_Array::unparse() { std::string result = "[ "; - size_t size = this->elements.size(); - for (size_t i = 0; i < size; ++i) { - result += this->elements.at(i).unparse(); - result += " "; + if (sparse) { + int next = 0; + for (auto& item: sp_elements) { + int key = item.first; + for (int j = next; j < key; ++j) { + result += "null "; + } + item.second->resolve(); + auto og = item.second->getObjGen(); + result += og.isIndirect() ? og.unparse(' ') + " R " + : item.second->unparse() + " "; + next = ++key; + } + for (int j = next; j < sp_size; ++j) { + result += "null "; + } + } else { + for (auto const& item: elements) { + item->resolve(); + auto og = item->getObjGen(); + result += og.isIndirect() ? og.unparse(' ') + " R " + : item->unparse() + " "; + } } result += "]"; return result; @@ -69,96 +160,157 @@ QPDF_Array::unparse() JSON QPDF_Array::getJSON(int json_version) { - JSON j = JSON::makeArray(); - size_t size = this->elements.size(); - for (size_t i = 0; i < size; ++i) { - j.addArrayElement(this->elements.at(i).getJSON(json_version)); + static const JSON j_null = JSON::makeNull(); + JSON j_array = JSON::makeArray(); + if (sparse) { + int next = 0; + for (auto& item: sp_elements) { + int key = item.first; + for (int j = next; j < key; ++j) { + j_array.addArrayElement(j_null); + } + auto og = item.second->getObjGen(); + j_array.addArrayElement( + og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R") + : item.second->getJSON(json_version)); + next = ++key; + } + for (int j = next; j < sp_size; ++j) { + j_array.addArrayElement(j_null); + } + } else { + for (auto const& item: elements) { + auto og = item->getObjGen(); + j_array.addArrayElement( + og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R") + : item->getJSON(json_version)); + } } - return j; -} - -int -QPDF_Array::getNItems() const -{ - // This should really return a size_t, but changing it would break - // a lot of code. - return QIntC::to_int(this->elements.size()); + return j_array; } QPDFObjectHandle -QPDF_Array::getItem(int n) const +QPDF_Array::at(int n) const noexcept { - if ((n < 0) || (n >= QIntC::to_int(elements.size()))) { - throw std::logic_error( - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); + if (n < 0 || n >= size()) { + return {}; + } else if (sparse) { + auto const& iter = sp_elements.find(n); + return iter == sp_elements.end() ? null_oh : (*iter).second; + } else { + return elements[size_t(n)]; } - return this->elements.at(QIntC::to_size(n)); } -void -QPDF_Array::getAsVector(std::vector<QPDFObjectHandle>& v) const +std::vector<QPDFObjectHandle> +QPDF_Array::getAsVector() const { - size_t size = this->elements.size(); - for (size_t i = 0; i < size; ++i) { - v.push_back(this->elements.at(i)); + if (sparse) { + std::vector<QPDFObjectHandle> v; + v.reserve(size_t(size())); + for (auto const& item: sp_elements) { + v.resize(size_t(item.first), null_oh); + v.push_back(item.second); + } + v.resize(size_t(size()), null_oh); + return v; + } else { + return {elements.cbegin(), elements.cend()}; } } -void -QPDF_Array::setItem(int n, QPDFObjectHandle const& oh) +bool +QPDF_Array::setAt(int at, QPDFObjectHandle const& oh) { - this->elements.setAt(QIntC::to_size(n), oh); + if (at < 0 || at >= size()) { + return false; + } + checkOwnership(oh); + if (sparse) { + sp_elements[at] = oh.getObj(); + } else { + elements[size_t(at)] = oh.getObj(); + } + return true; } void QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v) { - this->elements = SparseOHArray(); - for (auto const& iter: v) { - this->elements.append(iter); + elements.resize(0); + elements.reserve(v.size()); + for (auto const& item: v) { + checkOwnership(item); + elements.push_back(item.getObj()); } } -void -QPDF_Array::setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& v) +bool +QPDF_Array::insert(int at, QPDFObjectHandle const& item) { - this->elements = SparseOHArray(); - for (auto&& item: v) { - if (item) { - this->elements.append(item); + int sz = size(); + if (at < 0 || at > sz) { + // As special case, also allow insert beyond the end + return false; + } else if (at == sz) { + push_back(item); + } else { + checkOwnership(item); + if (sparse) { + auto iter = sp_elements.crbegin(); + while (iter != sp_elements.crend()) { + auto key = (iter++)->first; + if (key >= at) { + auto nh = sp_elements.extract(key); + ++nh.key(); + sp_elements.insert(std::move(nh)); + } else { + break; + } + } + sp_elements[at] = item.getObj(); + ++sp_size; } else { - ++this->elements.n_elements; + elements.insert(elements.cbegin() + at, item.getObj()); } } + return true; } void -QPDF_Array::insertItem(int at, QPDFObjectHandle const& item) +QPDF_Array::push_back(QPDFObjectHandle const& item) { - // As special case, also allow insert beyond the end - if ((at < 0) || (at > QIntC::to_int(this->elements.size()))) { - throw std::logic_error( - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); + checkOwnership(item); + if (sparse) { + sp_elements[sp_size++] = item.getObj(); + } else { + elements.push_back(item.getObj()); } - this->elements.insert(QIntC::to_size(at), item); } -void -QPDF_Array::appendItem(QPDFObjectHandle const& item) +bool +QPDF_Array::erase(int at) { - this->elements.append(item); -} - -void -QPDF_Array::eraseItem(int at) -{ - this->elements.erase(QIntC::to_size(at)); -} + if (at < 0 || at >= size()) { + return false; + } + if (sparse) { + auto end = sp_elements.end(); + if (auto iter = sp_elements.lower_bound(at); iter != end) { + if (iter->first == at) { + iter++; + sp_elements.erase(at); + } -void -QPDF_Array::addExplicitElementsToList(std::list<QPDFObjectHandle>& l) const -{ - for (auto const& iter: this->elements) { - l.push_back(iter.second); + while (iter != end) { + auto nh = sp_elements.extract(iter++); + --nh.key(); + sp_elements.insert(std::move(nh)); + } + } + --sp_size; + } else { + elements.erase(elements.cbegin() + at); } + return true; } diff --git a/libqpdf/QPDF_Null.cc b/libqpdf/QPDF_Null.cc index a82f23c0..0b59f5c9 100644 --- a/libqpdf/QPDF_Null.cc +++ b/libqpdf/QPDF_Null.cc @@ -50,5 +50,6 @@ QPDF_Null::unparse() JSON QPDF_Null::getJSON(int json_version) { + // If this is updated, QPDF_Array::getJSON must also be updated. return JSON::makeNull(); } diff --git a/libqpdf/SparseOHArray.cc b/libqpdf/SparseOHArray.cc deleted file mode 100644 index 5f64f50b..00000000 --- a/libqpdf/SparseOHArray.cc +++ /dev/null @@ -1,148 +0,0 @@ -#include <qpdf/SparseOHArray.hh> - -#include <qpdf/QPDFObjectHandle.hh> -#include <qpdf/QPDFObject_private.hh> - -#include <stdexcept> - -SparseOHArray::SparseOHArray() : - n_elements(0) -{ -} - -size_t -SparseOHArray::size() const -{ - return this->n_elements; -} - -void -SparseOHArray::append(QPDFObjectHandle oh) -{ - if (!oh.isDirectNull()) { - this->elements[this->n_elements] = oh; - } - ++this->n_elements; -} - -void -SparseOHArray::append(std::shared_ptr<QPDFObject>&& obj) -{ - if (obj->getTypeCode() != ::ot_null || !obj->getObjGen().isIndirect()) { - this->elements[this->n_elements] = std::move(obj); - } - ++this->n_elements; -} - -QPDFObjectHandle -SparseOHArray::at(size_t idx) const -{ - if (idx >= this->n_elements) { - throw std::logic_error( - "INTERNAL ERROR: bounds error accessing SparseOHArray element"); - } - auto const& iter = this->elements.find(idx); - if (iter == this->elements.end()) { - return QPDFObjectHandle::newNull(); - } else { - return (*iter).second; - } -} - -void -SparseOHArray::remove_last() -{ - if (this->n_elements == 0) { - throw std::logic_error("INTERNAL ERROR: attempt to remove" - " last item from empty SparseOHArray"); - } - --this->n_elements; - this->elements.erase(this->n_elements); -} - -void -SparseOHArray::disconnect() -{ - for (auto& iter: this->elements) { - QPDFObjectHandle::DisconnectAccess::disconnect(iter.second); - } -} - -void -SparseOHArray::setAt(size_t idx, QPDFObjectHandle oh) -{ - if (idx >= this->n_elements) { - throw std::logic_error("bounds error setting item in SparseOHArray"); - } - if (oh.isDirectNull()) { - this->elements.erase(idx); - } else { - this->elements[idx] = oh; - } -} - -void -SparseOHArray::erase(size_t idx) -{ - if (idx >= this->n_elements) { - throw std::logic_error("bounds error erasing item from SparseOHArray"); - } - decltype(this->elements) dest; - for (auto const& iter: this->elements) { - if (iter.first < idx) { - dest.insert(iter); - } else if (iter.first > idx) { - dest[iter.first - 1] = iter.second; - } - } - this->elements = dest; - --this->n_elements; -} - -void -SparseOHArray::insert(size_t idx, QPDFObjectHandle oh) -{ - if (idx > this->n_elements) { - throw std::logic_error("bounds error inserting item to SparseOHArray"); - } else if (idx == this->n_elements) { - // Allow inserting to the last position - append(oh); - } else { - decltype(this->elements) dest; - for (auto const& iter: this->elements) { - if (iter.first < idx) { - dest.insert(iter); - } else { - dest[iter.first + 1] = iter.second; - } - } - this->elements = dest; - this->elements[idx] = oh; - ++this->n_elements; - } -} - -SparseOHArray -SparseOHArray::copy() -{ - SparseOHArray result; - result.n_elements = this->n_elements; - for (auto const& element: this->elements) { - auto value = element.second; - result.elements[element.first] = - value.isIndirect() ? value : value.shallowCopy(); - } - return result; -} - -SparseOHArray::const_iterator -SparseOHArray::begin() const -{ - return this->elements.begin(); -} - -SparseOHArray::const_iterator -SparseOHArray::end() const -{ - return this->elements.end(); -} diff --git a/libqpdf/qpdf/QPDF_Array.hh b/libqpdf/qpdf/QPDF_Array.hh index 56c0101f..4762bb6e 100644 --- a/libqpdf/qpdf/QPDF_Array.hh +++ b/libqpdf/qpdf/QPDF_Array.hh @@ -3,8 +3,7 @@ #include <qpdf/QPDFValue.hh> -#include <qpdf/SparseOHArray.hh> -#include <list> +#include <map> #include <vector> class QPDF_Array: public QPDFValue @@ -14,34 +13,37 @@ class QPDF_Array: public QPDFValue static std::shared_ptr<QPDFObject> create(std::vector<QPDFObjectHandle> const& items); static std::shared_ptr<QPDFObject> - create(std::vector<std::shared_ptr<QPDFObject>>&& items); - static std::shared_ptr<QPDFObject> create(SparseOHArray const& items); + create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); virtual std::shared_ptr<QPDFObject> copy(bool shallow = false); virtual std::string unparse(); virtual JSON getJSON(int json_version); virtual void disconnect(); - int getNItems() const; - QPDFObjectHandle getItem(int n) const; - void getAsVector(std::vector<QPDFObjectHandle>&) const; - - void setItem(int, QPDFObjectHandle const&); + int + size() const noexcept + { + return sparse ? sp_size : int(elements.size()); + } + QPDFObjectHandle at(int n) const noexcept; + bool setAt(int n, QPDFObjectHandle const& oh); + std::vector<QPDFObjectHandle> getAsVector() const; void setFromVector(std::vector<QPDFObjectHandle> const& items); - void setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& items); - void insertItem(int at, QPDFObjectHandle const& item); - void appendItem(QPDFObjectHandle const& item); - void eraseItem(int at); - - // Helper methods for QPDF and QPDFObjectHandle -- these are - // public methods since the whole class is not part of the public - // API. Otherwise, these would be wrapped in accessor classes. - void addExplicitElementsToList(std::list<QPDFObjectHandle>&) const; + bool insert(int at, QPDFObjectHandle const& item); + void push_back(QPDFObjectHandle const& item); + bool erase(int at); private: + QPDF_Array(); + QPDF_Array(QPDF_Array const&); QPDF_Array(std::vector<QPDFObjectHandle> const& items); - QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items); - QPDF_Array(SparseOHArray const& items); - SparseOHArray elements; + QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); + + void checkOwnership(QPDFObjectHandle const& item) const; + + bool sparse{false}; + int sp_size{0}; + std::map<int, std::shared_ptr<QPDFObject>> sp_elements; + std::vector<std::shared_ptr<QPDFObject>> elements; }; #endif // QPDF_ARRAY_HH diff --git a/libqpdf/qpdf/SparseOHArray.hh b/libqpdf/qpdf/SparseOHArray.hh deleted file mode 100644 index 26ae3dc0..00000000 --- a/libqpdf/qpdf/SparseOHArray.hh +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef QPDF_SPARSEOHARRAY_HH -#define QPDF_SPARSEOHARRAY_HH - -#include <qpdf/QPDFObjectHandle.hh> -#include <unordered_map> - -class QPDF_Array; - -class SparseOHArray -{ - public: - SparseOHArray(); - size_t size() const; - void append(QPDFObjectHandle oh); - void append(std::shared_ptr<QPDFObject>&& obj); - QPDFObjectHandle at(size_t idx) const; - void remove_last(); - void setAt(size_t idx, QPDFObjectHandle oh); - void erase(size_t idx); - void insert(size_t idx, QPDFObjectHandle oh); - SparseOHArray copy(); - void disconnect(); - - typedef std::unordered_map<size_t, QPDFObjectHandle>::const_iterator - const_iterator; - const_iterator begin() const; - const_iterator end() const; - - private: - friend class QPDF_Array; - std::unordered_map<size_t, QPDFObjectHandle> elements; - size_t n_elements; -}; - -#endif // QPDF_SPARSEOHARRAY_HH |