2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 10:58:58 +00:00

Refactor Xref_table::Entry

Replace QPDFXRefEntry with a std::variant of structs.
This commit is contained in:
m-holger 2024-08-19 12:36:38 +01:00
parent 6b9eb14c76
commit a1b646fcca
2 changed files with 107 additions and 43 deletions

View File

@ -574,7 +574,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e)
// Delete all references to type 1 (uncompressed) objects // Delete all references to type 1 (uncompressed) objects
for (auto& iter: table) { for (auto& iter: table) {
if (iter.entry.getType() == 1) { if (iter.type() == 1) {
iter = {}; iter = {};
} }
} }
@ -638,12 +638,12 @@ QPDF::Xref_table::reconstruct(QPDFExc& e)
qpdf_offset_t max_offset{0}; qpdf_offset_t max_offset{0};
// If there are any xref streams, take the last one to appear. // If there are any xref streams, take the last one to appear.
int i = -1; int i = -1;
for (auto const& [gen, entry]: table) { for (auto const& item: table) {
++i; ++i;
if (entry.getType() != 1) { if (item.type() != 1) {
continue; continue;
} }
auto oh = qpdf.getObject(i, gen); auto oh = qpdf.getObject(i, item.gen());
try { try {
if (!oh.isStreamOfType("/XRef")) { if (!oh.isStreamOfType("/XRef")) {
continue; continue;
@ -651,7 +651,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e)
} catch (std::exception&) { } catch (std::exception&) {
continue; continue;
} }
auto offset = entry.getOffset(); auto offset = item.offset();
if (offset > max_offset) { if (offset > max_offset) {
max_offset = offset; max_offset = offset;
trailer_ = oh.getDict(); 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<size_t>(obj)]; auto& entry = table[static_cast<size_t>(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 // 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 // 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 // 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; return;
} }
if (old_type && entry.gen >= new_gen) { if (old_type && entry.gen() >= new_gen) {
QTC::TC("qpdf", "QPDF xref reused object"); QTC::TC("qpdf", "QPDF xref reused object");
return; return;
} }
@ -1360,11 +1360,11 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2)
case 1: case 1:
// f2 is generation // f2 is generation
QTC::TC("qpdf", "QPDF xref gen > 0", (f2 > 0) ? 1 : 0); QTC::TC("qpdf", "QPDF xref gen > 0", (f2 > 0) ? 1 : 0);
entry = {f2, QPDFXRefEntry(f1)}; entry = {f2, Uncompressed(f1)};
break; break;
case 2: case 2:
entry = {0, QPDFXRefEntry(toI(f1), f2)}; entry = {0, Compressed(toI(f1), f2)};
break; break;
default: default:
@ -1400,19 +1400,18 @@ QPDF::Xref_table::show()
{ {
auto& cout = *qpdf.m->log->getInfo(); auto& cout = *qpdf.m->log->getInfo();
int i = -1; int i = -1;
for (auto const& [gen, entry]: table) { for (auto const& item: table) {
++i; ++i;
auto type = entry.getType(); if (item.type()) {
if (type) { cout << std::to_string(i) << "/" << std::to_string(item.gen()) << ": ";
cout << std::to_string(i) << "/" << std::to_string(gen) << ": "; switch (item.type()) {
switch (type) {
case 1: case 1:
cout << "uncompressed; offset = " << entry.getOffset() << "\n"; cout << "uncompressed; offset = " << item.offset() << "\n";
break; break;
case 2: case 2:
cout << "compressed; stream = " << entry.getObjStreamNumber() cout << "compressed; stream = " << item.stream_number()
<< ", index = " << entry.getObjStreamIndex() << "\n"; << ", index = " << item.stream_index() << "\n";
break; break;
default: default:
@ -1430,11 +1429,11 @@ QPDF::Xref_table::resolve()
{ {
bool may_change = !reconstructed_; bool may_change = !reconstructed_;
int i = -1; int i = -1;
for (auto& iter: table) { for (auto& item: table) {
++i; ++i;
if (iter.entry.getType()) { if (item.type()) {
if (qpdf.isUnresolved(QPDFObjGen(i, iter.gen))) { if (qpdf.isUnresolved(QPDFObjGen(i, item.gen()))) {
qpdf.resolve(QPDFObjGen(i, iter.gen)); qpdf.resolve(QPDFObjGen(i, item.gen()));
if (may_change && reconstructed_) { if (may_change && reconstructed_) {
return false; return false;
} }

View File

@ -3,6 +3,8 @@
#include <qpdf/QPDF.hh> #include <qpdf/QPDF.hh>
#include <variant>
// Xref_table encapsulates the pdf's xref table and trailer. // Xref_table encapsulates the pdf's xref table and trailer.
class QPDF::Xref_table class QPDF::Xref_table
{ {
@ -34,54 +36,55 @@ class QPDF::Xref_table
} }
// Returns 0 if og is not in table. // Returns 0 if og is not in table.
int size_t
type(QPDFObjGen og) const type(QPDFObjGen og) const
{ {
if (og.getObj() >= toI(table.size())) { int id = og.getObj();
if (id < 1 || static_cast<size_t>(id) >= table.size()) {
return 0; return 0;
} }
auto& e = table.at(toS(og.getObj())); auto& e = table[static_cast<size_t>(id)];
return e.gen == og.getGen() ? e.entry.getType() : 0; return e.gen() == og.getGen() ? e.type() : 0;
} }
// Returns 0 if og is not in table. // Returns 0 if og is not in table.
int size_t
type(size_t id) const type(size_t id) const noexcept
{ {
if (id >= table.size()) { if (id >= table.size()) {
return 0; return 0;
} }
return table[id].entry.getType(); return table[id].type();
} }
// Returns 0 if og is not in table. // Returns 0 if og is not in table.
qpdf_offset_t 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<size_t>(id) >= table.size()) {
return 0; return 0;
} }
auto& e = table.at(toS(og.getObj())); return table[static_cast<size_t>(id)].offset();
return e.gen == og.getGen() ? e.entry.getOffset() : 0;
} }
// Returns 0 if og is not in table. // Returns 0 if id is not in table.
int int
stream_number(int id) const stream_number(int id) const noexcept
{ {
if (id < 1 || static_cast<size_t>(id) >= table.size()) { if (id < 1 || static_cast<size_t>(id) >= table.size()) {
return 0; return 0;
} }
return table[static_cast<size_t>(id)].entry.getObjStreamNumber(); return table[static_cast<size_t>(id)].stream_number();
} }
int int
stream_index(int id) const stream_index(int id) const noexcept
{ {
if (id < 1 || static_cast<size_t>(id) >= table.size()) { if (id < 1 || static_cast<size_t>(id) >= table.size()) {
return 0; return 0;
} }
return table[static_cast<size_t>(id)].entry.getObjStreamIndex(); return table[static_cast<size_t>(id)].stream_index();
} }
// Temporary access to underlying map // Temporary access to underlying map
@ -90,9 +93,19 @@ class QPDF::Xref_table
{ {
std::map<QPDFObjGen, QPDFXRefEntry> result; std::map<QPDFObjGen, QPDFXRefEntry> result;
int i{0}; int i{0};
for (auto const& [gen, entry]: table) { for (auto const& item: table) {
if (entry.getType()) { switch (item.type()) {
result.emplace(QPDFObjGen(i, gen), entry); 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; ++i;
} }
@ -149,10 +162,62 @@ class QPDF::Xref_table
// Object, count, offset of first entry // Object, count, offset of first entry
typedef std::tuple<int, int, qpdf_offset_t> Subsection; typedef std::tuple<int, int, qpdf_offset_t> 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<std::monostate, Uncompressed, Compressed> Xref;
struct Entry struct Entry
{ {
int gen{0}; int
QPDFXRefEntry entry; 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); void read(qpdf_offset_t offset);