#ifndef OBJTABLE_HH #define OBJTABLE_HH #include #include #include "qpdf/QIntC.hh" #include // A table of objects indexed by object id. This is intended as a more efficient replacement for // std::map containers. // // The table is implemented as a std::vector, with the object id implicitly represented by the index // of the object. This has a number of implications, including: // - operations that change the index of existing elements such as insertion and deletions are not // permitted. // - operations that extend the table may invalidate iterators and references to objects. // // The provided overloads of the access operator[] are safe. For out of bounds access they will // either extend the table or throw a runtime error. // // ObjTable has a map 'sparse_elements' to deal with very sparse / extremely large object tables // (usually as the result of invalid dangling references). This map may contain objects not found in // the xref table of the original pdf if there are dangling references with an id significantly // larger than the largest valid object id found in original pdf. template class ObjTable: public std::vector { public: ObjTable() = default; ObjTable(const ObjTable&) = delete; ObjTable(ObjTable&&) = delete; ObjTable& operator[](const ObjTable&) = delete; ObjTable& operator[](ObjTable&&) = delete; // Remove unchecked access. T& operator[](unsigned long idx) = delete; T const& operator[](unsigned long idx) const = delete; inline T const& operator[](int idx) const { return element(static_cast(idx)); } inline T const& operator[](QPDFObjGen og) const { return element(static_cast(og.getObj())); } inline T const& operator[](QPDFObjectHandle oh) const { return element(static_cast(oh.getObjectID())); } inline bool contains(size_t idx) const { return idx < std::vector::size() || sparse_elements.count(idx); } inline bool contains(QPDFObjGen og) const { return contains(static_cast(og.getObj())); } inline bool contains(QPDFObjectHandle oh) const { return contains(static_cast(oh.getObjectID())); } protected: inline T& operator[](int id) { return element(static_cast(id)); } inline T& operator[](QPDFObjGen og) { return element(static_cast(og.getObj())); } inline T& operator[](QPDFObjectHandle oh) { return element(static_cast(oh.getObjectID())); } inline T& operator[](unsigned int id) { return element(id); } void initialize(size_t idx) { if (std::vector::size() > 0 || sparse_elements.size() > 0) { throw ::std::logic_error("ObjTable accessed before initialization"); } else if ( idx >= static_cast(std::numeric_limits::max()) || idx >= std::vector::max_size()) { throw std::runtime_error("Invalid maximum object id initializing ObjTable."); } else { std::vector::resize(++idx); } } inline void forEach(std::function fn) { int i = 0; for (auto const& item: *this) { fn(i++, item); } for (auto const& [id, item]: sparse_elements) { fn(QIntC::to_int(id), item); } } private: std::map sparse_elements; inline T& element(size_t idx) { if (idx < std::vector::size()) { return std::vector::operator[](idx); } else if (idx < static_cast(std::numeric_limits::max())) { return sparse_elements[idx]; } throw std::runtime_error("Invalid object id accessing ObjTable."); return element(0); // doesn't return } inline T const& element(size_t idx) const { if (idx < std::vector::size()) { return std::vector::operator[](idx); } else if (idx < static_cast(std::numeric_limits::max())) { return sparse_elements.at(idx); } throw std::runtime_error("Invalid object id accessing ObjTable."); return element(0); // doesn't return } }; #endif // OBJTABLE_HH