Commit acc57ca09095d5e6247893a54d94e99ba2304c52

Authored by m-holger
1 parent 336d7833

Add QPDF::Objects destructor

Also, make obj_cache private and rename to table.
libqpdf/QPDF.cc
@@ -199,25 +199,7 @@ QPDF::QPDF() : @@ -199,25 +199,7 @@ QPDF::QPDF() :
199 m->unique_id = unique_id.fetch_add(1ULL); 199 m->unique_id = unique_id.fetch_add(1ULL);
200 } 200 }
201 201
202 -QPDF::~QPDF()  
203 -{  
204 - // If two objects are mutually referential (through each object having an array or dictionary  
205 - // that contains an indirect reference to the other), the circular references in the  
206 - // std::shared_ptr objects will prevent the objects from being deleted. Walk through all objects  
207 - // in the object cache, which is those objects that we read from the file, and break all  
208 - // resolved indirect references by replacing them with an internal object type representing that  
209 - // they have been destroyed. Note that we can't break references like this at any time when the  
210 - // QPDF object is active. The call to reset also causes all direct QPDFObjectHandle objects that  
211 - // are reachable from this object to release their association with this QPDF. Direct objects  
212 - // are not destroyed since they can be moved to other QPDF objects safely.  
213 -  
214 - for (auto const& iter: m->objects.obj_cache) {  
215 - iter.second.object->disconnect();  
216 - if (iter.second.object->getTypeCode() != ::ot_null) {  
217 - iter.second.object->destroy();  
218 - }  
219 - }  
220 -} 202 +QPDF::~QPDF() = default;
221 203
222 std::shared_ptr<QPDF> 204 std::shared_ptr<QPDF>
223 QPDF::create() 205 QPDF::create()
libqpdf/QPDF_objects.cc
@@ -1154,7 +1154,7 @@ Objects ::all() @@ -1154,7 +1154,7 @@ Objects ::all()
1154 // After fixDanglingReferences is called, all objects are in the object cache. 1154 // After fixDanglingReferences is called, all objects are in the object cache.
1155 qpdf.fixDanglingReferences(); 1155 qpdf.fixDanglingReferences();
1156 std::vector<QPDFObjectHandle> result; 1156 std::vector<QPDFObjectHandle> result;
1157 - for (auto const& iter: obj_cache) { 1157 + for (auto const& iter: table) {
1158 result.emplace_back(iter.second.object); 1158 result.emplace_back(iter.second.object);
1159 } 1159 }
1160 return result; 1160 return result;
@@ -1539,7 +1539,7 @@ QPDFObject* @@ -1539,7 +1539,7 @@ QPDFObject*
1539 Objects::resolve(QPDFObjGen og) 1539 Objects::resolve(QPDFObjGen og)
1540 { 1540 {
1541 if (!unresolved(og)) { 1541 if (!unresolved(og)) {
1542 - return obj_cache[og].object.get(); 1542 + return table[og].object.get();
1543 } 1543 }
1544 1544
1545 if (m->resolving.count(og)) { 1545 if (m->resolving.count(og)) {
@@ -1548,7 +1548,7 @@ Objects::resolve(QPDFObjGen og) @@ -1548,7 +1548,7 @@ Objects::resolve(QPDFObjGen og)
1548 QTC::TC("qpdf", "QPDF recursion loop in resolve"); 1548 QTC::TC("qpdf", "QPDF recursion loop in resolve");
1549 qpdf.warn(qpdf.damagedPDF("", "loop detected resolving object " + og.unparse(' '))); 1549 qpdf.warn(qpdf.damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
1550 update_table(og, QPDF_Null::create()); 1550 update_table(og, QPDF_Null::create());
1551 - return obj_cache[og].object.get(); 1551 + return table[og].object.get();
1552 } 1552 }
1553 ResolveRecorder rr(&qpdf, og); 1553 ResolveRecorder rr(&qpdf, og);
1554 1554
@@ -1585,7 +1585,7 @@ Objects::resolve(QPDFObjGen og) @@ -1585,7 +1585,7 @@ Objects::resolve(QPDFObjGen og)
1585 update_table(og, QPDF_Null::create()); 1585 update_table(og, QPDF_Null::create());
1586 } 1586 }
1587 1587
1588 - auto result(obj_cache[og].object); 1588 + auto result(table[og].object);
1589 result->setDefaultDescription(&qpdf, og); 1589 result->setDefaultDescription(&qpdf, og);
1590 return result.get(); 1590 return result.get();
1591 } 1591 }
@@ -1685,28 +1685,48 @@ Objects::resolveObjectsInStream(int obj_stream_number) @@ -1685,28 +1685,48 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1685 } 1685 }
1686 } 1686 }
1687 1687
  1688 +Objects::~Objects()
  1689 +{
  1690 + // If two objects are mutually referential (through each object having an array or dictionary
  1691 + // that contains an indirect reference to the other), the circular references in the
  1692 + // std::shared_ptr objects will prevent the objects from being deleted. Walk through all objects
  1693 + // in the object cache, which is those objects that we read from the file, and break all
  1694 + // resolved indirect references by replacing them with an internal object type representing that
  1695 + // they have been destroyed. Note that we can't break references like this at any time when the
  1696 + // QPDF object is active. The call to reset also causes all direct QPDFObjectHandle objects that
  1697 + // are reachable from this object to release their association with this QPDF. Direct objects
  1698 + // are not destroyed since they can be moved to other QPDF objects safely.
  1699 +
  1700 + for (auto const& iter: table) {
  1701 + iter.second.object->disconnect();
  1702 + if (iter.second.object->getTypeCode() != ::ot_null) {
  1703 + iter.second.object->destroy();
  1704 + }
  1705 + }
  1706 +}
  1707 +
1688 void 1708 void
1689 Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& object) 1709 Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& object)
1690 { 1710 {
1691 object->setObjGen(&qpdf, og); 1711 object->setObjGen(&qpdf, og);
1692 if (cached(og)) { 1712 if (cached(og)) {
1693 - auto& cache = obj_cache[og]; 1713 + auto& cache = table[og];
1694 cache.object->assign(object); 1714 cache.object->assign(object);
1695 } else { 1715 } else {
1696 - obj_cache[og] = Entry(object); 1716 + table[og] = Entry(object);
1697 } 1717 }
1698 } 1718 }
1699 1719
1700 bool 1720 bool
1701 Objects::cached(QPDFObjGen og) 1721 Objects::cached(QPDFObjGen og)
1702 { 1722 {
1703 - return obj_cache.count(og) != 0; 1723 + return table.count(og) != 0;
1704 } 1724 }
1705 1725
1706 bool 1726 bool
1707 Objects::unresolved(QPDFObjGen og) 1727 Objects::unresolved(QPDFObjGen og)
1708 { 1728 {
1709 - return !cached(og) || obj_cache[og].object->isUnresolved(); 1729 + return !cached(og) || table[og].object->isUnresolved();
1710 } 1730 }
1711 1731
1712 QPDFObjGen 1732 QPDFObjGen
@@ -1714,8 +1734,8 @@ Objects::next_id() @@ -1714,8 +1734,8 @@ Objects::next_id()
1714 { 1734 {
1715 qpdf.fixDanglingReferences(); 1735 qpdf.fixDanglingReferences();
1716 QPDFObjGen og; 1736 QPDFObjGen og;
1717 - if (!obj_cache.empty()) {  
1718 - og = (*(m->objects.obj_cache.rbegin())).first; 1737 + if (!table.empty()) {
  1738 + og = (*(m->objects.table.rbegin())).first;
1719 } 1739 }
1720 int max_objid = og.getObj(); 1740 int max_objid = og.getObj();
1721 if (max_objid == std::numeric_limits<int>::max()) { 1741 if (max_objid == std::numeric_limits<int>::max()) {
@@ -1728,8 +1748,8 @@ QPDFObjectHandle @@ -1728,8 +1748,8 @@ QPDFObjectHandle
1728 Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj) 1748 Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj)
1729 { 1749 {
1730 QPDFObjGen next{next_id()}; 1750 QPDFObjGen next{next_id()};
1731 - obj_cache[next] = Entry(obj);  
1732 - return qpdf.newIndirect(next, obj_cache[next].object); 1751 + table[next] = Entry(obj);
  1752 + return qpdf.newIndirect(next, table[next].object);
1733 } 1753 }
1734 1754
1735 std::shared_ptr<QPDFObject> 1755 std::shared_ptr<QPDFObject>
@@ -1737,23 +1757,23 @@ Objects::get_for_parser(int id, int gen, bool parse_pdf) @@ -1737,23 +1757,23 @@ Objects::get_for_parser(int id, int gen, bool parse_pdf)
1737 { 1757 {
1738 // This method is called by the parser and therefore must not resolve any objects. 1758 // This method is called by the parser and therefore must not resolve any objects.
1739 auto og = QPDFObjGen(id, gen); 1759 auto og = QPDFObjGen(id, gen);
1740 - if (auto iter = obj_cache.find(og); iter != obj_cache.end()) { 1760 + if (auto iter = table.find(og); iter != table.end()) {
1741 return iter->second.object; 1761 return iter->second.object;
1742 } 1762 }
1743 if (xref.type(og) || !xref.initialized()) { 1763 if (xref.type(og) || !xref.initialized()) {
1744 - return obj_cache.insert({og, QPDF_Unresolved::create(&qpdf, og)}).first->second.object; 1764 + return table.insert({og, QPDF_Unresolved::create(&qpdf, og)}).first->second.object;
1745 } 1765 }
1746 if (parse_pdf) { 1766 if (parse_pdf) {
1747 return QPDF_Null::create(); 1767 return QPDF_Null::create();
1748 } 1768 }
1749 - return obj_cache.insert({og, QPDF_Null::create(&qpdf, og)}).first->second.object; 1769 + return table.insert({og, QPDF_Null::create(&qpdf, og)}).first->second.object;
1750 } 1770 }
1751 1771
1752 std::shared_ptr<QPDFObject> 1772 std::shared_ptr<QPDFObject>
1753 Objects::get_for_json(int id, int gen) 1773 Objects::get_for_json(int id, int gen)
1754 { 1774 {
1755 auto og = QPDFObjGen(id, gen); 1775 auto og = QPDFObjGen(id, gen);
1756 - auto [it, inserted] = obj_cache.try_emplace(og); 1776 + auto [it, inserted] = table.try_emplace(og);
1757 auto& obj = it->second.object; 1777 auto& obj = it->second.object;
1758 if (inserted) { 1778 if (inserted) {
1759 obj = (xref.initialized() && !xref.type(og)) ? QPDF_Null::create(&qpdf, og) 1779 obj = (xref.initialized() && !xref.type(og)) ? QPDF_Null::create(&qpdf, og)
@@ -1775,11 +1795,11 @@ Objects::replace(QPDFObjGen og, QPDFObjectHandle oh) @@ -1775,11 +1795,11 @@ Objects::replace(QPDFObjGen og, QPDFObjectHandle oh)
1775 void 1795 void
1776 Objects::erase(QPDFObjGen og) 1796 Objects::erase(QPDFObjGen og)
1777 { 1797 {
1778 - if (auto cached = obj_cache.find(og); cached != obj_cache.end()) { 1798 + if (auto cached = table.find(og); cached != table.end()) {
1779 // Take care of any object handles that may be floating around. 1799 // Take care of any object handles that may be floating around.
1780 cached->second.object->assign(QPDF_Null::create()); 1800 cached->second.object->assign(QPDF_Null::create());
1781 cached->second.object->setObjGen(nullptr, QPDFObjGen()); 1801 cached->second.object->setObjGen(nullptr, QPDFObjGen());
1782 - obj_cache.erase(cached); 1802 + table.erase(cached);
1783 } 1803 }
1784 } 1804 }
1785 1805
@@ -1789,19 +1809,19 @@ Objects::swap(QPDFObjGen og1, QPDFObjGen og2) @@ -1789,19 +1809,19 @@ Objects::swap(QPDFObjGen og1, QPDFObjGen og2)
1789 // Force objects to be read from the input source if needed, then swap them in the cache. 1809 // Force objects to be read from the input source if needed, then swap them in the cache.
1790 resolve(og1); 1810 resolve(og1);
1791 resolve(og2); 1811 resolve(og2);
1792 - obj_cache[og1].object->swapWith(obj_cache[og2].object); 1812 + table[og1].object->swapWith(table[og2].object);
1793 } 1813 }
1794 1814
1795 size_t 1815 size_t
1796 Objects::table_size() 1816 Objects::table_size()
1797 { 1817 {
1798 - // If obj_cache is dense, accommodate all object in tables,else accommodate only original 1818 + // If table is dense, accommodate all object in tables,else accommodate only original
1799 // objects. 1819 // objects.
1800 auto max_xref = toI(xref.size()); 1820 auto max_xref = toI(xref.size());
1801 if (max_xref > 0) { 1821 if (max_xref > 0) {
1802 --max_xref; 1822 --max_xref;
1803 } 1823 }
1804 - auto max_obj = obj_cache.size() ? obj_cache.crbegin()->first.getObj() : 0; 1824 + auto max_obj = table.size() ? table.crbegin()->first.getObj() : 0;
1805 auto max_id = std::numeric_limits<int>::max() - 1; 1825 auto max_id = std::numeric_limits<int>::max() - 1;
1806 if (max_obj >= max_id || max_xref >= max_id) { 1826 if (max_obj >= max_id || max_xref >= max_id) {
1807 // Temporary fix. Long-term solution is 1827 // Temporary fix. Long-term solution is
@@ -1809,7 +1829,7 @@ Objects::table_size() @@ -1809,7 +1829,7 @@ Objects::table_size()
1809 // - xref table and obj cache to protect against insertion of impossibly large obj ids 1829 // - xref table and obj cache to protect against insertion of impossibly large obj ids
1810 qpdf.stopOnError("Impossibly large object id encountered."); 1830 qpdf.stopOnError("Impossibly large object id encountered.");
1811 } 1831 }
1812 - if (max_obj < 1.1 * std::max(toI(obj_cache.size()), max_xref)) { 1832 + if (max_obj < 1.1 * std::max(toI(table.size()), max_xref)) {
1813 return toS(++max_obj); 1833 return toS(++max_obj);
1814 } 1834 }
1815 return toS(++max_xref); 1835 return toS(++max_xref);
@@ -1848,7 +1868,7 @@ Objects::compressible() @@ -1848,7 +1868,7 @@ Objects::compressible()
1848 queue.emplace_back(trailer()); 1868 queue.emplace_back(trailer());
1849 std::vector<T> result; 1869 std::vector<T> result;
1850 if constexpr (std::is_same_v<T, QPDFObjGen>) { 1870 if constexpr (std::is_same_v<T, QPDFObjGen>) {
1851 - result.reserve(obj_cache.size()); 1871 + result.reserve(table.size());
1852 } else if constexpr (std::is_same_v<T, bool>) { 1872 } else if constexpr (std::is_same_v<T, bool>) {
1853 result.resize(max_obj + 1U, false); 1873 result.resize(max_obj + 1U, false);
1854 } else { 1874 } else {
@@ -1872,8 +1892,8 @@ Objects::compressible() @@ -1872,8 +1892,8 @@ Objects::compressible()
1872 // Check whether this is the current object. If not, remove it (which changes it into a 1892 // Check whether this is the current object. If not, remove it (which changes it into a
1873 // direct null and therefore stops us from revisiting it) and move on to the next object 1893 // direct null and therefore stops us from revisiting it) and move on to the next object
1874 // in the queue. 1894 // in the queue.
1875 - auto upper = obj_cache.upper_bound(og);  
1876 - if (upper != obj_cache.end() && upper->first.getObj() == og.getObj()) { 1895 + auto upper = table.upper_bound(og);
  1896 + if (upper != table.end() && upper->first.getObj() == og.getObj()) {
1877 erase(og); 1897 erase(og);
1878 continue; 1898 continue;
1879 } 1899 }
libqpdf/qpdf/QPDF_objects.hh
@@ -378,17 +378,7 @@ class QPDF::Objects @@ -378,17 +378,7 @@ class QPDF::Objects
378 qpdf_offset_t first_item_offset_{0}; // actual value from file 378 qpdf_offset_t first_item_offset_{0}; // actual value from file
379 }; // Xref_table; 379 }; // Xref_table;
380 380
381 - struct Entry  
382 - {  
383 - Entry() = default;  
384 -  
385 - Entry(std::shared_ptr<QPDFObject> object) :  
386 - object(std::move(object))  
387 - {  
388 - }  
389 -  
390 - std::shared_ptr<QPDFObject> object;  
391 - }; 381 + ~Objects();
392 382
393 Objects(QPDF& qpdf, QPDF::Members* m, InputSource* const& file) : 383 Objects(QPDF& qpdf, QPDF::Members* m, InputSource* const& file) :
394 qpdf(qpdf), 384 qpdf(qpdf),
@@ -425,12 +415,12 @@ class QPDF::Objects @@ -425,12 +415,12 @@ class QPDF::Objects
425 QPDFObjectHandle 415 QPDFObjectHandle
426 get(QPDFObjGen og) 416 get(QPDFObjGen og)
427 { 417 {
428 - if (auto it = obj_cache.find(og); it != obj_cache.end()) { 418 + if (auto it = table.find(og); it != table.end()) {
429 return {it->second.object}; 419 return {it->second.object};
430 } else if (xref.initialized() && !xref.type(og)) { 420 } else if (xref.initialized() && !xref.type(og)) {
431 return QPDF_Null::create(); 421 return QPDF_Null::create();
432 } else { 422 } else {
433 - auto result = obj_cache.try_emplace(og, QPDF_Unresolved::create(&qpdf, og)); 423 + auto result = table.try_emplace(og, QPDF_Unresolved::create(&qpdf, og));
434 return {result.first->second.object}; 424 return {result.first->second.object};
435 } 425 }
436 } 426 }
@@ -449,9 +439,6 @@ class QPDF::Objects @@ -449,9 +439,6 @@ class QPDF::Objects
449 439
450 void swap(QPDFObjGen og1, QPDFObjGen og2); 440 void swap(QPDFObjGen og1, QPDFObjGen og2);
451 441
452 - std::map<QPDFObjGen, Entry> obj_cache;  
453 -  
454 - QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>& input, int obj);  
455 QPDFObjectHandle read( 442 QPDFObjectHandle read(
456 bool attempt_recovery, 443 bool attempt_recovery,
457 qpdf_offset_t offset, 444 qpdf_offset_t offset,
@@ -460,7 +447,6 @@ class QPDF::Objects @@ -460,7 +447,6 @@ class QPDF::Objects
460 QPDFObjGen& og, 447 QPDFObjGen& og,
461 bool skip_cache_if_in_xref); 448 bool skip_cache_if_in_xref);
462 QPDFObject* resolve(QPDFObjGen og); 449 QPDFObject* resolve(QPDFObjGen og);
463 - void resolveObjectsInStream(int obj_stream_number);  
464 void update_table(QPDFObjGen og, std::shared_ptr<QPDFObject> const& object); 450 void update_table(QPDFObjGen og, std::shared_ptr<QPDFObject> const& object);
465 QPDFObjGen next_id(); 451 QPDFObjGen next_id();
466 QPDFObjectHandle make_indirect(std::shared_ptr<QPDFObject> const& obj); 452 QPDFObjectHandle make_indirect(std::shared_ptr<QPDFObject> const& obj);
@@ -477,9 +463,23 @@ class QPDF::Objects @@ -477,9 +463,23 @@ class QPDF::Objects
477 size_t table_size(); 463 size_t table_size();
478 464
479 private: 465 private:
  466 + struct Entry
  467 + {
  468 + Entry() = default;
  469 +
  470 + Entry(std::shared_ptr<QPDFObject> object) :
  471 + object(object)
  472 + {
  473 + }
  474 +
  475 + std::shared_ptr<QPDFObject> object;
  476 + };
  477 +
480 bool cached(QPDFObjGen og); 478 bool cached(QPDFObjGen og);
481 bool unresolved(QPDFObjGen og); 479 bool unresolved(QPDFObjGen og);
482 480
  481 + QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>& input, int obj);
  482 + void resolveObjectsInStream(int obj_stream_number);
483 QPDFObjectHandle read_object(std::string const& description, QPDFObjGen og); 483 QPDFObjectHandle read_object(std::string const& description, QPDFObjGen og);
484 void read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); 484 void read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
485 void validate_stream_line_end(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); 485 void validate_stream_line_end(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
@@ -490,6 +490,8 @@ class QPDF::Objects @@ -490,6 +490,8 @@ class QPDF::Objects
490 InputSource* const& file; 490 InputSource* const& file;
491 QPDF::Members* m; 491 QPDF::Members* m;
492 Xref_table xref; 492 Xref_table xref;
  493 +
  494 + std::map<QPDFObjGen, Entry> table;
493 }; // Objects 495 }; // Objects
494 496
495 #endif // QPDF_OBJECTS_HH 497 #endif // QPDF_OBJECTS_HH