Skip to content
forked from qpdf/qpdf

Commit

Permalink
Add new inner class to QPDF::Objects
Browse files Browse the repository at this point in the history
  • Loading branch information
m-holger committed Oct 7, 2024
1 parent 83897e8 commit 2015f71
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 30 deletions.
2 changes: 2 additions & 0 deletions include/qpdf/QPDF.hh
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ class QPDF
class Writer;
class Resolver;
class StreamCopier;
class Objects;
class ParseGuard;
class Pipe;
class JobSetter;
Expand Down Expand Up @@ -757,6 +758,7 @@ class QPDF
class ResolveRecorder;
class JSONReactor;

inline Objects& objects();
void parse(char const* password);
void inParse(bool);
void setLastObjectDescription(std::string const& description, QPDFObjGen const& og);
Expand Down
13 changes: 7 additions & 6 deletions libqpdf/QPDF.cc
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ QPDF::Members::Members(QPDF& qpdf) :
file_sp(new InvalidInputSource(no_input_name)),
file(file_sp.get()),
encp(new EncryptionParameters),
xref_table(qpdf, file)
objects(qpdf, this),
xref_table(qpdf, objects, file)
{
}

Expand All @@ -211,7 +212,7 @@ QPDF::~QPDF()
// are reachable from this object to release their association with this QPDF. Direct objects
// are not destroyed since they can be moved to other QPDF objects safely.

for (auto const& iter: m->obj_cache) {
for (auto const& iter: m->objects.obj_cache) {
iter.second.object->disconnect();
if (iter.second.object->getTypeCode() != ::ot_null) {
iter.second.object->destroy();
Expand Down Expand Up @@ -494,8 +495,8 @@ QPDF::getObjectCount()
// be in obj_cache.
fixDanglingReferences();
QPDFObjGen og;
if (!m->obj_cache.empty()) {
og = (*(m->obj_cache.rbegin())).first;
if (!m->objects.obj_cache.empty()) {
og = (*(m->objects.obj_cache.rbegin())).first;
}
return toS(og.getObj());
}
Expand Down Expand Up @@ -575,12 +576,12 @@ QPDF::newStream(std::string const& data)
QPDFObjectHandle
QPDF::getObject(QPDFObjGen const& og)
{
if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) {
if (auto it = m->objects.obj_cache.find(og); it != m->objects.obj_cache.end()) {
return {it->second.object};
} else if (m->xref_table.initialized() && !m->xref_table.type(og)) {
return QPDF_Null::create();
} else {
auto result = m->obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og));
auto result = m->objects.obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og));
return {result.first->second.object};
}
}
Expand Down
45 changes: 23 additions & 22 deletions libqpdf/QPDF_objects.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@ QPDF::getAllObjects()
// After fixDanglingReferences is called, all objects are in the object cache.
fixDanglingReferences();
std::vector<QPDFObjectHandle> result;
for (auto const& iter: m->obj_cache) {
for (auto const& iter: m->objects.obj_cache) {
result.push_back(newIndirect(iter.first, iter.second.object));
}
return result;
Expand Down Expand Up @@ -1537,7 +1537,7 @@ QPDFObject*
QPDF::resolve(QPDFObjGen og)
{
if (!isUnresolved(og)) {
return m->obj_cache[og].object.get();
return m->objects.obj_cache[og].object.get();
}

if (m->resolving.count(og)) {
Expand All @@ -1546,7 +1546,7 @@ QPDF::resolve(QPDFObjGen og)
QTC::TC("qpdf", "QPDF recursion loop in resolve");
warn(damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
updateCache(og, QPDF_Null::create());
return m->obj_cache[og].object.get();
return m->objects.obj_cache[og].object.get();
}
ResolveRecorder rr(this, og);

Expand Down Expand Up @@ -1584,7 +1584,7 @@ QPDF::resolve(QPDFObjGen og)
updateCache(og, QPDF_Null::create());
}

auto result(m->obj_cache[og].object);
auto result(m->objects.obj_cache[og].object);
result->setDefaultDescription(this, og);
return result.get();
}
Expand Down Expand Up @@ -1690,23 +1690,23 @@ QPDF::updateCache(QPDFObjGen const& og, std::shared_ptr<QPDFObject> const& objec
{
object->setObjGen(this, og);
if (isCached(og)) {
auto& cache = m->obj_cache[og];
auto& cache = m->objects.obj_cache[og];
cache.object->assign(object);
} else {
m->obj_cache[og] = ObjCache(object);
m->objects.obj_cache[og] = ObjCache(object);
}
}

bool
QPDF::isCached(QPDFObjGen const& og)
{
return m->obj_cache.count(og) != 0;
return m->objects.obj_cache.count(og) != 0;
}

bool
QPDF::isUnresolved(QPDFObjGen const& og)
{
return !isCached(og) || m->obj_cache[og].object->isUnresolved();
return !isCached(og) || m->objects.obj_cache[og].object->isUnresolved();
}

QPDFObjGen
Expand All @@ -1723,32 +1723,33 @@ QPDFObjectHandle
QPDF::makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj)
{
QPDFObjGen next{nextObjGen()};
m->obj_cache[next] = ObjCache(obj);
return newIndirect(next, m->obj_cache[next].object);
m->objects.obj_cache[next] = ObjCache(obj);
return newIndirect(next, m->objects.obj_cache[next].object);
}

std::shared_ptr<QPDFObject>
QPDF::getObjectForParser(int id, int gen, bool parse_pdf)
{
// This method is called by the parser and therefore must not resolve any objects.
auto og = QPDFObjGen(id, gen);
if (auto iter = m->obj_cache.find(og); iter != m->obj_cache.end()) {
if (auto iter = m->objects.obj_cache.find(og); iter != m->objects.obj_cache.end()) {
return iter->second.object;
}
if (m->xref_table.type(og) || !m->xref_table.initialized()) {
return m->obj_cache.insert({og, QPDF_Unresolved::create(this, og)}).first->second.object;
return m->objects.obj_cache.insert({og, QPDF_Unresolved::create(this, og)})
.first->second.object;
}
if (parse_pdf) {
return QPDF_Null::create();
}
return m->obj_cache.insert({og, QPDF_Null::create(this, og)}).first->second.object;
return m->objects.obj_cache.insert({og, QPDF_Null::create(this, og)}).first->second.object;
}

std::shared_ptr<QPDFObject>
QPDF::getObjectForJSON(int id, int gen)
{
auto og = QPDFObjGen(id, gen);
auto [it, inserted] = m->obj_cache.try_emplace(og);
auto [it, inserted] = m->objects.obj_cache.try_emplace(og);
auto& obj = it->second.object;
if (inserted) {
obj = (m->xref_table.initialized() && !m->xref_table.type(og))
Expand All @@ -1771,11 +1772,11 @@ QPDF::replaceObject(QPDFObjGen const& og, QPDFObjectHandle oh)
void
QPDF::removeObject(QPDFObjGen og)
{
if (auto cached = m->obj_cache.find(og); cached != m->obj_cache.end()) {
if (auto cached = m->objects.obj_cache.find(og); cached != m->objects.obj_cache.end()) {
// Take care of any object handles that may be floating around.
cached->second.object->assign(QPDF_Null::create());
cached->second.object->setObjGen(nullptr, QPDFObjGen());
m->obj_cache.erase(cached);
m->objects.obj_cache.erase(cached);
}
}

Expand All @@ -1785,7 +1786,7 @@ QPDF::swapObjects(QPDFObjGen const& og1, QPDFObjGen const& og2)
// Force objects to be read from the input source if needed, then swap them in the cache.
resolve(og1);
resolve(og2);
m->obj_cache[og1].object->swapWith(m->obj_cache[og2].object);
m->objects.obj_cache[og1].object->swapWith(m->objects.obj_cache[og2].object);
}

size_t
Expand All @@ -1797,15 +1798,15 @@ QPDF::tableSize()
if (max_xref > 0) {
--max_xref;
}
auto max_obj = m->obj_cache.size() ? m->obj_cache.crbegin()->first.getObj() : 0;
auto max_obj = m->objects.obj_cache.size() ? m->objects.obj_cache.crbegin()->first.getObj() : 0;
auto max_id = std::numeric_limits<int>::max() - 1;
if (max_obj >= max_id || max_xref >= max_id) {
// Temporary fix. Long-term solution is
// - QPDFObjGen to enforce objgens are valid and sensible
// - xref table and obj cache to protect against insertion of impossibly large obj ids
stopOnError("Impossibly large object id encountered.");
}
if (max_obj < 1.1 * std::max(toI(m->obj_cache.size()), max_xref)) {
if (max_obj < 1.1 * std::max(toI(m->objects.obj_cache.size()), max_xref)) {
return toS(++max_obj);
}
return toS(++max_xref);
Expand Down Expand Up @@ -1844,7 +1845,7 @@ QPDF::getCompressibleObjGens()
queue.push_back(m->xref_table.trailer());
std::vector<T> result;
if constexpr (std::is_same_v<T, QPDFObjGen>) {
result.reserve(m->obj_cache.size());
result.reserve(m->objects.obj_cache.size());
} else if constexpr (std::is_same_v<T, bool>) {
result.resize(max_obj + 1U, false);
} else {
Expand All @@ -1868,8 +1869,8 @@ QPDF::getCompressibleObjGens()
// Check whether this is the current object. If not, remove it (which changes it into a
// direct null and therefore stops us from revisiting it) and move on to the next object
// in the queue.
auto upper = m->obj_cache.upper_bound(og);
if (upper != m->obj_cache.end() && upper->first.getObj() == og.getObj()) {
auto upper = m->objects.obj_cache.upper_bound(og);
if (upper != m->objects.obj_cache.end() && upper->first.getObj() == og.getObj()) {
removeObject(og);
continue;
}
Expand Down
18 changes: 18 additions & 0 deletions libqpdf/qpdf/QPDF_objects.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef QPDF_OBJECTS_HH
#define QPDF_OBJECTS_HH

#include <qpdf/QPDF.hh>

// The Objects class is responsible for keeping track of all objects belonging to a QPDF instance,
// including loading it from an input source when required.
class QPDF::Objects
{
public:
Objects(QPDF& qpdf, QPDF::Members* m)
{
}

std::map<QPDFObjGen, ObjCache> obj_cache;
}; // Objects

#endif // QPDF_OBJECTS_HH
12 changes: 10 additions & 2 deletions libqpdf/qpdf/QPDF_private.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

#include <qpdf/QPDF.hh>

#include <qpdf/QPDF_objects.hh>

#include <variant>

// Xref_table encapsulates the pdf's xref table and trailer.
class QPDF::Xref_table
{
public:
Xref_table(QPDF& qpdf, InputSource* const& file) :
Xref_table(QPDF& qpdf, QPDF::Objects& objects, InputSource* const& file) :
qpdf(qpdf),
file(file)
{
Expand Down Expand Up @@ -750,8 +752,8 @@ class QPDF::Members
bool check_mode{false};
std::shared_ptr<EncryptionParameters> encp;
std::string pdf_version;
Objects objects;
Xref_table xref_table;
std::map<QPDFObjGen, ObjCache> obj_cache;
std::set<QPDFObjGen> resolving;
std::vector<QPDFObjectHandle> all_pages;
bool invalid_page_found{false};
Expand Down Expand Up @@ -800,6 +802,12 @@ class QPDF::Members
std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users;
};

inline QPDF::Objects&
QPDF::objects()
{
return m->objects;
}

// JobSetter class is restricted to QPDFJob.
class QPDF::JobSetter
{
Expand Down

0 comments on commit 2015f71

Please sign in to comment.