From a1b646fccafbdbc39c78f2bad4c21aa507f0477f Mon Sep 17 00:00:00 2001 From: m-holger Date: Mon, 19 Aug 2024 12:36:38 +0100 Subject: [PATCH] Refactor Xref_table::Entry Replace QPDFXRefEntry with a std::variant of structs. --- libqpdf/QPDF.cc | 43 +++++++------- libqpdf/qpdf/QPDF_private.hh | 107 ++++++++++++++++++++++++++++------- 2 files changed, 107 insertions(+), 43 deletions(-) diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index c39de8e8..abd8335d 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -574,7 +574,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) // Delete all references to type 1 (uncompressed) objects for (auto& iter: table) { - if (iter.entry.getType() == 1) { + if (iter.type() == 1) { iter = {}; } } @@ -638,12 +638,12 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) qpdf_offset_t max_offset{0}; // If there are any xref streams, take the last one to appear. int i = -1; - for (auto const& [gen, entry]: table) { + for (auto const& item: table) { ++i; - if (entry.getType() != 1) { + if (item.type() != 1) { continue; } - auto oh = qpdf.getObject(i, gen); + auto oh = qpdf.getObject(i, item.gen()); try { if (!oh.isStreamOfType("/XRef")) { continue; @@ -651,7 +651,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) } catch (std::exception&) { continue; } - auto offset = entry.getOffset(); + auto offset = item.offset(); if (offset > max_offset) { max_offset = offset; trailer_ = oh.getDict(); @@ -1334,9 +1334,9 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) } auto& entry = table[static_cast(obj)]; - auto old_type = entry.entry.getType(); + auto old_type = entry.type(); - if (!old_type && entry.gen > 0) { + if (!old_type && entry.gen() > 0) { // At the moment we are processing the updates last to first and therefore the gen doesn't // matter as long as it > 0 to distinguish it from an uninitialized entry. This will need // to be revisited when we want to support incremental updates or more comprhensive @@ -1351,7 +1351,7 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) return; } - if (old_type && entry.gen >= new_gen) { + if (old_type && entry.gen() >= new_gen) { QTC::TC("qpdf", "QPDF xref reused object"); return; } @@ -1360,11 +1360,11 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) case 1: // f2 is generation QTC::TC("qpdf", "QPDF xref gen > 0", (f2 > 0) ? 1 : 0); - entry = {f2, QPDFXRefEntry(f1)}; + entry = {f2, Uncompressed(f1)}; break; case 2: - entry = {0, QPDFXRefEntry(toI(f1), f2)}; + entry = {0, Compressed(toI(f1), f2)}; break; default: @@ -1400,19 +1400,18 @@ QPDF::Xref_table::show() { auto& cout = *qpdf.m->log->getInfo(); int i = -1; - for (auto const& [gen, entry]: table) { + for (auto const& item: table) { ++i; - auto type = entry.getType(); - if (type) { - cout << std::to_string(i) << "/" << std::to_string(gen) << ": "; - switch (type) { + if (item.type()) { + cout << std::to_string(i) << "/" << std::to_string(item.gen()) << ": "; + switch (item.type()) { case 1: - cout << "uncompressed; offset = " << entry.getOffset() << "\n"; + cout << "uncompressed; offset = " << item.offset() << "\n"; break; case 2: - cout << "compressed; stream = " << entry.getObjStreamNumber() - << ", index = " << entry.getObjStreamIndex() << "\n"; + cout << "compressed; stream = " << item.stream_number() + << ", index = " << item.stream_index() << "\n"; break; default: @@ -1430,11 +1429,11 @@ QPDF::Xref_table::resolve() { bool may_change = !reconstructed_; int i = -1; - for (auto& iter: table) { + for (auto& item: table) { ++i; - if (iter.entry.getType()) { - if (qpdf.isUnresolved(QPDFObjGen(i, iter.gen))) { - qpdf.resolve(QPDFObjGen(i, iter.gen)); + if (item.type()) { + if (qpdf.isUnresolved(QPDFObjGen(i, item.gen()))) { + qpdf.resolve(QPDFObjGen(i, item.gen())); if (may_change && reconstructed_) { return false; } diff --git a/libqpdf/qpdf/QPDF_private.hh b/libqpdf/qpdf/QPDF_private.hh index 21cbf7c9..0516dc05 100644 --- a/libqpdf/qpdf/QPDF_private.hh +++ b/libqpdf/qpdf/QPDF_private.hh @@ -3,6 +3,8 @@ #include +#include + // Xref_table encapsulates the pdf's xref table and trailer. class QPDF::Xref_table { @@ -34,54 +36,55 @@ class QPDF::Xref_table } // Returns 0 if og is not in table. - int + size_t type(QPDFObjGen og) const { - if (og.getObj() >= toI(table.size())) { + int id = og.getObj(); + if (id < 1 || static_cast(id) >= table.size()) { return 0; } - auto& e = table.at(toS(og.getObj())); - return e.gen == og.getGen() ? e.entry.getType() : 0; + auto& e = table[static_cast(id)]; + return e.gen() == og.getGen() ? e.type() : 0; } // Returns 0 if og is not in table. - int - type(size_t id) const + size_t + type(size_t id) const noexcept { if (id >= table.size()) { return 0; } - return table[id].entry.getType(); + return table[id].type(); } // Returns 0 if og is not in table. qpdf_offset_t - offset(QPDFObjGen og) const + offset(QPDFObjGen og) const noexcept { - if (og.getObj() >= toI(table.size())) { + int id = og.getObj(); + if (id < 1 || static_cast(id) >= table.size()) { return 0; } - auto& e = table.at(toS(og.getObj())); - return e.gen == og.getGen() ? e.entry.getOffset() : 0; + return table[static_cast(id)].offset(); } - // Returns 0 if og is not in table. + // Returns 0 if id is not in table. int - stream_number(int id) const + stream_number(int id) const noexcept { if (id < 1 || static_cast(id) >= table.size()) { return 0; } - return table[static_cast(id)].entry.getObjStreamNumber(); + return table[static_cast(id)].stream_number(); } int - stream_index(int id) const + stream_index(int id) const noexcept { if (id < 1 || static_cast(id) >= table.size()) { return 0; } - return table[static_cast(id)].entry.getObjStreamIndex(); + return table[static_cast(id)].stream_index(); } // Temporary access to underlying map @@ -90,9 +93,19 @@ class QPDF::Xref_table { std::map result; int i{0}; - for (auto const& [gen, entry]: table) { - if (entry.getType()) { - result.emplace(QPDFObjGen(i, gen), entry); + for (auto const& item: table) { + switch (item.type()) { + case 0: + break; + case 1: + result.emplace(QPDFObjGen(i, item.gen()), item.offset()); + break; + case 2: + result.emplace( + QPDFObjGen(i, 0), QPDFXRefEntry(item.stream_number(), item.stream_index())); + break; + default: + throw std::logic_error("Xref_table: invalid entry type"); } ++i; } @@ -149,10 +162,62 @@ class QPDF::Xref_table // Object, count, offset of first entry typedef std::tuple Subsection; + struct Uncompressed + { + Uncompressed(qpdf_offset_t offset) : + offset(offset) + { + } + qpdf_offset_t offset; + }; + + struct Compressed + { + Compressed(int stream_number, int stream_index) : + stream_number(stream_number), + stream_index(stream_index) + { + } + int stream_number{0}; + int stream_index{0}; + }; + + typedef std::variant Xref; + struct Entry { - int gen{0}; - QPDFXRefEntry entry; + int + gen() const noexcept + { + return gen_; + } + + size_t + type() const noexcept + { + return entry.index(); + } + + qpdf_offset_t + offset() const noexcept + { + return type() == 1 ? std::get<1>(entry).offset : 0; + } + + int + stream_number() const noexcept + { + return type() == 2 ? std::get<2>(entry).stream_number : 0; + } + + int + stream_index() const noexcept + { + return type() == 2 ? std::get<2>(entry).stream_index : 0; + } + + int gen_{0}; + Xref entry; }; void read(qpdf_offset_t offset);