Commit a1b646fccafbdbc39c78f2bad4c21aa507f0477f
1 parent
6b9eb14c
Refactor Xref_table::Entry
Replace QPDFXRefEntry with a std::variant of structs.
Showing
2 changed files
with
107 additions
and
43 deletions
libqpdf/QPDF.cc
| ... | ... | @@ -574,7 +574,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) |
| 574 | 574 | |
| 575 | 575 | // Delete all references to type 1 (uncompressed) objects |
| 576 | 576 | for (auto& iter: table) { |
| 577 | - if (iter.entry.getType() == 1) { | |
| 577 | + if (iter.type() == 1) { | |
| 578 | 578 | iter = {}; |
| 579 | 579 | } |
| 580 | 580 | } |
| ... | ... | @@ -638,12 +638,12 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) |
| 638 | 638 | qpdf_offset_t max_offset{0}; |
| 639 | 639 | // If there are any xref streams, take the last one to appear. |
| 640 | 640 | int i = -1; |
| 641 | - for (auto const& [gen, entry]: table) { | |
| 641 | + for (auto const& item: table) { | |
| 642 | 642 | ++i; |
| 643 | - if (entry.getType() != 1) { | |
| 643 | + if (item.type() != 1) { | |
| 644 | 644 | continue; |
| 645 | 645 | } |
| 646 | - auto oh = qpdf.getObject(i, gen); | |
| 646 | + auto oh = qpdf.getObject(i, item.gen()); | |
| 647 | 647 | try { |
| 648 | 648 | if (!oh.isStreamOfType("/XRef")) { |
| 649 | 649 | continue; |
| ... | ... | @@ -651,7 +651,7 @@ QPDF::Xref_table::reconstruct(QPDFExc& e) |
| 651 | 651 | } catch (std::exception&) { |
| 652 | 652 | continue; |
| 653 | 653 | } |
| 654 | - auto offset = entry.getOffset(); | |
| 654 | + auto offset = item.offset(); | |
| 655 | 655 | if (offset > max_offset) { |
| 656 | 656 | max_offset = offset; |
| 657 | 657 | trailer_ = oh.getDict(); |
| ... | ... | @@ -1334,9 +1334,9 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) |
| 1334 | 1334 | } |
| 1335 | 1335 | |
| 1336 | 1336 | auto& entry = table[static_cast<size_t>(obj)]; |
| 1337 | - auto old_type = entry.entry.getType(); | |
| 1337 | + auto old_type = entry.type(); | |
| 1338 | 1338 | |
| 1339 | - if (!old_type && entry.gen > 0) { | |
| 1339 | + if (!old_type && entry.gen() > 0) { | |
| 1340 | 1340 | // At the moment we are processing the updates last to first and therefore the gen doesn't |
| 1341 | 1341 | // matter as long as it > 0 to distinguish it from an uninitialized entry. This will need |
| 1342 | 1342 | // 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) |
| 1351 | 1351 | return; |
| 1352 | 1352 | } |
| 1353 | 1353 | |
| 1354 | - if (old_type && entry.gen >= new_gen) { | |
| 1354 | + if (old_type && entry.gen() >= new_gen) { | |
| 1355 | 1355 | QTC::TC("qpdf", "QPDF xref reused object"); |
| 1356 | 1356 | return; |
| 1357 | 1357 | } |
| ... | ... | @@ -1360,11 +1360,11 @@ QPDF::Xref_table::insert(int obj, int f0, qpdf_offset_t f1, int f2) |
| 1360 | 1360 | case 1: |
| 1361 | 1361 | // f2 is generation |
| 1362 | 1362 | QTC::TC("qpdf", "QPDF xref gen > 0", (f2 > 0) ? 1 : 0); |
| 1363 | - entry = {f2, QPDFXRefEntry(f1)}; | |
| 1363 | + entry = {f2, Uncompressed(f1)}; | |
| 1364 | 1364 | break; |
| 1365 | 1365 | |
| 1366 | 1366 | case 2: |
| 1367 | - entry = {0, QPDFXRefEntry(toI(f1), f2)}; | |
| 1367 | + entry = {0, Compressed(toI(f1), f2)}; | |
| 1368 | 1368 | break; |
| 1369 | 1369 | |
| 1370 | 1370 | default: |
| ... | ... | @@ -1400,19 +1400,18 @@ QPDF::Xref_table::show() |
| 1400 | 1400 | { |
| 1401 | 1401 | auto& cout = *qpdf.m->log->getInfo(); |
| 1402 | 1402 | int i = -1; |
| 1403 | - for (auto const& [gen, entry]: table) { | |
| 1403 | + for (auto const& item: table) { | |
| 1404 | 1404 | ++i; |
| 1405 | - auto type = entry.getType(); | |
| 1406 | - if (type) { | |
| 1407 | - cout << std::to_string(i) << "/" << std::to_string(gen) << ": "; | |
| 1408 | - switch (type) { | |
| 1405 | + if (item.type()) { | |
| 1406 | + cout << std::to_string(i) << "/" << std::to_string(item.gen()) << ": "; | |
| 1407 | + switch (item.type()) { | |
| 1409 | 1408 | case 1: |
| 1410 | - cout << "uncompressed; offset = " << entry.getOffset() << "\n"; | |
| 1409 | + cout << "uncompressed; offset = " << item.offset() << "\n"; | |
| 1411 | 1410 | break; |
| 1412 | 1411 | |
| 1413 | 1412 | case 2: |
| 1414 | - cout << "compressed; stream = " << entry.getObjStreamNumber() | |
| 1415 | - << ", index = " << entry.getObjStreamIndex() << "\n"; | |
| 1413 | + cout << "compressed; stream = " << item.stream_number() | |
| 1414 | + << ", index = " << item.stream_index() << "\n"; | |
| 1416 | 1415 | break; |
| 1417 | 1416 | |
| 1418 | 1417 | default: |
| ... | ... | @@ -1430,11 +1429,11 @@ QPDF::Xref_table::resolve() |
| 1430 | 1429 | { |
| 1431 | 1430 | bool may_change = !reconstructed_; |
| 1432 | 1431 | int i = -1; |
| 1433 | - for (auto& iter: table) { | |
| 1432 | + for (auto& item: table) { | |
| 1434 | 1433 | ++i; |
| 1435 | - if (iter.entry.getType()) { | |
| 1436 | - if (qpdf.isUnresolved(QPDFObjGen(i, iter.gen))) { | |
| 1437 | - qpdf.resolve(QPDFObjGen(i, iter.gen)); | |
| 1434 | + if (item.type()) { | |
| 1435 | + if (qpdf.isUnresolved(QPDFObjGen(i, item.gen()))) { | |
| 1436 | + qpdf.resolve(QPDFObjGen(i, item.gen())); | |
| 1438 | 1437 | if (may_change && reconstructed_) { |
| 1439 | 1438 | return false; |
| 1440 | 1439 | } | ... | ... |
libqpdf/qpdf/QPDF_private.hh
| ... | ... | @@ -3,6 +3,8 @@ |
| 3 | 3 | |
| 4 | 4 | #include <qpdf/QPDF.hh> |
| 5 | 5 | |
| 6 | +#include <variant> | |
| 7 | + | |
| 6 | 8 | // Xref_table encapsulates the pdf's xref table and trailer. |
| 7 | 9 | class QPDF::Xref_table |
| 8 | 10 | { |
| ... | ... | @@ -34,54 +36,55 @@ class QPDF::Xref_table |
| 34 | 36 | } |
| 35 | 37 | |
| 36 | 38 | // Returns 0 if og is not in table. |
| 37 | - int | |
| 39 | + size_t | |
| 38 | 40 | type(QPDFObjGen og) const |
| 39 | 41 | { |
| 40 | - if (og.getObj() >= toI(table.size())) { | |
| 42 | + int id = og.getObj(); | |
| 43 | + if (id < 1 || static_cast<size_t>(id) >= table.size()) { | |
| 41 | 44 | return 0; |
| 42 | 45 | } |
| 43 | - auto& e = table.at(toS(og.getObj())); | |
| 44 | - return e.gen == og.getGen() ? e.entry.getType() : 0; | |
| 46 | + auto& e = table[static_cast<size_t>(id)]; | |
| 47 | + return e.gen() == og.getGen() ? e.type() : 0; | |
| 45 | 48 | } |
| 46 | 49 | |
| 47 | 50 | // Returns 0 if og is not in table. |
| 48 | - int | |
| 49 | - type(size_t id) const | |
| 51 | + size_t | |
| 52 | + type(size_t id) const noexcept | |
| 50 | 53 | { |
| 51 | 54 | if (id >= table.size()) { |
| 52 | 55 | return 0; |
| 53 | 56 | } |
| 54 | - return table[id].entry.getType(); | |
| 57 | + return table[id].type(); | |
| 55 | 58 | } |
| 56 | 59 | |
| 57 | 60 | // Returns 0 if og is not in table. |
| 58 | 61 | qpdf_offset_t |
| 59 | - offset(QPDFObjGen og) const | |
| 62 | + offset(QPDFObjGen og) const noexcept | |
| 60 | 63 | { |
| 61 | - if (og.getObj() >= toI(table.size())) { | |
| 64 | + int id = og.getObj(); | |
| 65 | + if (id < 1 || static_cast<size_t>(id) >= table.size()) { | |
| 62 | 66 | return 0; |
| 63 | 67 | } |
| 64 | - auto& e = table.at(toS(og.getObj())); | |
| 65 | - return e.gen == og.getGen() ? e.entry.getOffset() : 0; | |
| 68 | + return table[static_cast<size_t>(id)].offset(); | |
| 66 | 69 | } |
| 67 | 70 | |
| 68 | - // Returns 0 if og is not in table. | |
| 71 | + // Returns 0 if id is not in table. | |
| 69 | 72 | int |
| 70 | - stream_number(int id) const | |
| 73 | + stream_number(int id) const noexcept | |
| 71 | 74 | { |
| 72 | 75 | if (id < 1 || static_cast<size_t>(id) >= table.size()) { |
| 73 | 76 | return 0; |
| 74 | 77 | } |
| 75 | - return table[static_cast<size_t>(id)].entry.getObjStreamNumber(); | |
| 78 | + return table[static_cast<size_t>(id)].stream_number(); | |
| 76 | 79 | } |
| 77 | 80 | |
| 78 | 81 | int |
| 79 | - stream_index(int id) const | |
| 82 | + stream_index(int id) const noexcept | |
| 80 | 83 | { |
| 81 | 84 | if (id < 1 || static_cast<size_t>(id) >= table.size()) { |
| 82 | 85 | return 0; |
| 83 | 86 | } |
| 84 | - return table[static_cast<size_t>(id)].entry.getObjStreamIndex(); | |
| 87 | + return table[static_cast<size_t>(id)].stream_index(); | |
| 85 | 88 | } |
| 86 | 89 | |
| 87 | 90 | // Temporary access to underlying map |
| ... | ... | @@ -90,9 +93,19 @@ class QPDF::Xref_table |
| 90 | 93 | { |
| 91 | 94 | std::map<QPDFObjGen, QPDFXRefEntry> result; |
| 92 | 95 | int i{0}; |
| 93 | - for (auto const& [gen, entry]: table) { | |
| 94 | - if (entry.getType()) { | |
| 95 | - result.emplace(QPDFObjGen(i, gen), entry); | |
| 96 | + for (auto const& item: table) { | |
| 97 | + switch (item.type()) { | |
| 98 | + case 0: | |
| 99 | + break; | |
| 100 | + case 1: | |
| 101 | + result.emplace(QPDFObjGen(i, item.gen()), item.offset()); | |
| 102 | + break; | |
| 103 | + case 2: | |
| 104 | + result.emplace( | |
| 105 | + QPDFObjGen(i, 0), QPDFXRefEntry(item.stream_number(), item.stream_index())); | |
| 106 | + break; | |
| 107 | + default: | |
| 108 | + throw std::logic_error("Xref_table: invalid entry type"); | |
| 96 | 109 | } |
| 97 | 110 | ++i; |
| 98 | 111 | } |
| ... | ... | @@ -149,10 +162,62 @@ class QPDF::Xref_table |
| 149 | 162 | // Object, count, offset of first entry |
| 150 | 163 | typedef std::tuple<int, int, qpdf_offset_t> Subsection; |
| 151 | 164 | |
| 165 | + struct Uncompressed | |
| 166 | + { | |
| 167 | + Uncompressed(qpdf_offset_t offset) : | |
| 168 | + offset(offset) | |
| 169 | + { | |
| 170 | + } | |
| 171 | + qpdf_offset_t offset; | |
| 172 | + }; | |
| 173 | + | |
| 174 | + struct Compressed | |
| 175 | + { | |
| 176 | + Compressed(int stream_number, int stream_index) : | |
| 177 | + stream_number(stream_number), | |
| 178 | + stream_index(stream_index) | |
| 179 | + { | |
| 180 | + } | |
| 181 | + int stream_number{0}; | |
| 182 | + int stream_index{0}; | |
| 183 | + }; | |
| 184 | + | |
| 185 | + typedef std::variant<std::monostate, Uncompressed, Compressed> Xref; | |
| 186 | + | |
| 152 | 187 | struct Entry |
| 153 | 188 | { |
| 154 | - int gen{0}; | |
| 155 | - QPDFXRefEntry entry; | |
| 189 | + int | |
| 190 | + gen() const noexcept | |
| 191 | + { | |
| 192 | + return gen_; | |
| 193 | + } | |
| 194 | + | |
| 195 | + size_t | |
| 196 | + type() const noexcept | |
| 197 | + { | |
| 198 | + return entry.index(); | |
| 199 | + } | |
| 200 | + | |
| 201 | + qpdf_offset_t | |
| 202 | + offset() const noexcept | |
| 203 | + { | |
| 204 | + return type() == 1 ? std::get<1>(entry).offset : 0; | |
| 205 | + } | |
| 206 | + | |
| 207 | + int | |
| 208 | + stream_number() const noexcept | |
| 209 | + { | |
| 210 | + return type() == 2 ? std::get<2>(entry).stream_number : 0; | |
| 211 | + } | |
| 212 | + | |
| 213 | + int | |
| 214 | + stream_index() const noexcept | |
| 215 | + { | |
| 216 | + return type() == 2 ? std::get<2>(entry).stream_index : 0; | |
| 217 | + } | |
| 218 | + | |
| 219 | + int gen_{0}; | |
| 220 | + Xref entry; | |
| 156 | 221 | }; |
| 157 | 222 | |
| 158 | 223 | void read(qpdf_offset_t offset); | ... | ... |