From 9b0801721710093102c64068b6c643c8fcd7f5db Mon Sep 17 00:00:00 2001 From: m-holger Date: Thu, 5 Jan 2023 11:09:44 +0000 Subject: [PATCH] Add new convenience class QPDFObjGen::set --- include/qpdf/QPDFObjGen.hh | 69 ++++++++++++++++++++++++++++++++++++-- libqpdf/QPDFObjGen.cc | 56 +++++++++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 4 deletions(-) diff --git a/include/qpdf/QPDFObjGen.hh b/include/qpdf/QPDFObjGen.hh index ccab4ba2..0d14efaf 100644 --- a/include/qpdf/QPDFObjGen.hh +++ b/include/qpdf/QPDFObjGen.hh @@ -24,6 +24,10 @@ #include #include +#include + +class QPDFObjectHandle; +class QPDFObjectHelper; // This class represents an object ID and generation pair. It is // suitable to use as a key in a map or set. @@ -31,6 +35,7 @@ class QPDFObjGen { public: + // ABI: change to default. QPDF_DLL QPDFObjGen() : obj(0), @@ -84,12 +89,72 @@ class QPDFObjGen QPDF_DLL friend std::ostream& operator<<(std::ostream& os, const QPDFObjGen& og); + // Convenience class for loop detection when processing objects. + // + // The class adds 'add' methods to a std::set which allows + // to test whether an QPDFObjGen is present in the set and to insert it in + // a single operation. The 'add' method is overloaded to take a QPDFObjGen, + // QPDFObjectHandle or an QPDFObjectHelper as parameter. + // + // The erase method is modified to ignore requests to erase + // QPDFObjGen(0, 0). + // + // Usage example: + // + // void process_object(QPDFObjectHandle oh, QPDFObjGen::Tracker& seen) + // { + // if (seen.add(oh)) { + // // handle first encounter of oh + // } else { + // // handle loop / subsequent encounter of oh + // } + // } + class QPDF_DLL_CLASS set: public std::set + { + public: + // Add 'og' to the set. Return false if 'og' is already present in + // the set. Attempts to insert QPDFObjGen(0, 0) are ignored. + QPDF_DLL + bool + add(QPDFObjGen og) + { + if (og.isIndirect()) { + if (count(og) > 0) { + return false; + } + emplace(og); + } + return true; + } + + QPDF_DLL + bool add(QPDFObjectHandle const& oh); + + QPDF_DLL + bool add(QPDFObjectHelper const& oh); + + QPDF_DLL + void + erase(QPDFObjGen og) + { + if (og.isIndirect()) { + std::set::erase(og); + } + } + + QPDF_DLL + void erase(QPDFObjectHandle const& oh); + + QPDF_DLL + void erase(QPDFObjectHelper const& oh); + }; + private: // This class does not use the Members pattern to avoid a memory // allocation for every one of these. A lot of these get created // and destroyed. - int obj; - int gen; + int obj{0}; + int gen{0}; }; #endif // QPDFOBJGEN_HH diff --git a/libqpdf/QPDFObjGen.cc b/libqpdf/QPDFObjGen.cc index 7cce84d8..8e5bd178 100644 --- a/libqpdf/QPDFObjGen.cc +++ b/libqpdf/QPDFObjGen.cc @@ -1,7 +1,12 @@ #include -#include +#include +#include +#include +#include + +// ABI: inline and pass og by value std::ostream& operator<<(std::ostream& os, const QPDFObjGen& og) { @@ -9,8 +14,55 @@ operator<<(std::ostream& os, const QPDFObjGen& og) return os; } +// ABI: inline std::string QPDFObjGen::unparse(char separator) const { - return std::to_string(this->obj) + separator + std::to_string(this->gen); + return std::to_string(obj) + separator + std::to_string(gen); +} + +bool +QPDFObjGen::set::add(QPDFObjectHandle const& oh) +{ + if (auto* ptr = oh.getObjectPtr()) { + return add(ptr->getObjGen()); + } else { + throw std::logic_error("attempt to retrieve QPDFObjGen from " + "uninitialized QPDFObjectHandle"); + return false; + } +} + +bool +QPDFObjGen::set::add(QPDFObjectHelper const& helper) +{ + if (auto* ptr = helper.getObjectHandle().getObjectPtr()) { + return add(ptr->getObjGen()); + } else { + throw std::logic_error("attempt to retrieve QPDFObjGen from " + "uninitialized QPDFObjectHandle"); + return false; + } +} + +void +QPDFObjGen::set::erase(QPDFObjectHandle const& oh) +{ + if (auto* ptr = oh.getObjectPtr()) { + erase(ptr->getObjGen()); + } else { + throw std::logic_error("attempt to retrieve QPDFObjGen from " + "uninitialized QPDFObjectHandle"); + } +} + +void +QPDFObjGen::set::erase(QPDFObjectHelper const& helper) +{ + if (auto* ptr = helper.getObjectHandle().getObjectPtr()) { + erase(ptr->getObjGen()); + } else { + throw std::logic_error("attempt to retrieve QPDFObjGen from " + "uninitialized QPDFObjectHandle"); + } }