2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 02:49:00 +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
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<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
// 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;
}

View File

@ -3,6 +3,8 @@
#include <qpdf/QPDF.hh>
#include <variant>
// 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<size_t>(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<size_t>(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<size_t>(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<size_t>(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<size_t>(id) >= table.size()) {
return 0;
}
return table[static_cast<size_t>(id)].entry.getObjStreamNumber();
return table[static_cast<size_t>(id)].stream_number();
}
int
stream_index(int id) const
stream_index(int id) const noexcept
{
if (id < 1 || static_cast<size_t>(id) >= table.size()) {
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
@ -90,9 +93,19 @@ class QPDF::Xref_table
{
std::map<QPDFObjGen, QPDFXRefEntry> 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<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
{
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);