Commit acc57ca09095d5e6247893a54d94e99ba2304c52
1 parent
336d7833
Add QPDF::Objects destructor
Also, make obj_cache private and rename to table.
Showing
3 changed files
with
65 additions
and
61 deletions
libqpdf/QPDF.cc
| ... | ... | @@ -199,25 +199,7 @@ QPDF::QPDF() : |
| 199 | 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 | 204 | std::shared_ptr<QPDF> |
| 223 | 205 | QPDF::create() | ... | ... |
libqpdf/QPDF_objects.cc
| ... | ... | @@ -1154,7 +1154,7 @@ Objects ::all() |
| 1154 | 1154 | // After fixDanglingReferences is called, all objects are in the object cache. |
| 1155 | 1155 | qpdf.fixDanglingReferences(); |
| 1156 | 1156 | std::vector<QPDFObjectHandle> result; |
| 1157 | - for (auto const& iter: obj_cache) { | |
| 1157 | + for (auto const& iter: table) { | |
| 1158 | 1158 | result.emplace_back(iter.second.object); |
| 1159 | 1159 | } |
| 1160 | 1160 | return result; |
| ... | ... | @@ -1539,7 +1539,7 @@ QPDFObject* |
| 1539 | 1539 | Objects::resolve(QPDFObjGen og) |
| 1540 | 1540 | { |
| 1541 | 1541 | if (!unresolved(og)) { |
| 1542 | - return obj_cache[og].object.get(); | |
| 1542 | + return table[og].object.get(); | |
| 1543 | 1543 | } |
| 1544 | 1544 | |
| 1545 | 1545 | if (m->resolving.count(og)) { |
| ... | ... | @@ -1548,7 +1548,7 @@ Objects::resolve(QPDFObjGen og) |
| 1548 | 1548 | QTC::TC("qpdf", "QPDF recursion loop in resolve"); |
| 1549 | 1549 | qpdf.warn(qpdf.damagedPDF("", "loop detected resolving object " + og.unparse(' '))); |
| 1550 | 1550 | update_table(og, QPDF_Null::create()); |
| 1551 | - return obj_cache[og].object.get(); | |
| 1551 | + return table[og].object.get(); | |
| 1552 | 1552 | } |
| 1553 | 1553 | ResolveRecorder rr(&qpdf, og); |
| 1554 | 1554 | |
| ... | ... | @@ -1585,7 +1585,7 @@ Objects::resolve(QPDFObjGen og) |
| 1585 | 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 | 1589 | result->setDefaultDescription(&qpdf, og); |
| 1590 | 1590 | return result.get(); |
| 1591 | 1591 | } |
| ... | ... | @@ -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 | 1708 | void |
| 1689 | 1709 | Objects::update_table(QPDFObjGen og, const std::shared_ptr<QPDFObject>& object) |
| 1690 | 1710 | { |
| 1691 | 1711 | object->setObjGen(&qpdf, og); |
| 1692 | 1712 | if (cached(og)) { |
| 1693 | - auto& cache = obj_cache[og]; | |
| 1713 | + auto& cache = table[og]; | |
| 1694 | 1714 | cache.object->assign(object); |
| 1695 | 1715 | } else { |
| 1696 | - obj_cache[og] = Entry(object); | |
| 1716 | + table[og] = Entry(object); | |
| 1697 | 1717 | } |
| 1698 | 1718 | } |
| 1699 | 1719 | |
| 1700 | 1720 | bool |
| 1701 | 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 | 1726 | bool |
| 1707 | 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 | 1732 | QPDFObjGen |
| ... | ... | @@ -1714,8 +1734,8 @@ Objects::next_id() |
| 1714 | 1734 | { |
| 1715 | 1735 | qpdf.fixDanglingReferences(); |
| 1716 | 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 | 1740 | int max_objid = og.getObj(); |
| 1721 | 1741 | if (max_objid == std::numeric_limits<int>::max()) { |
| ... | ... | @@ -1728,8 +1748,8 @@ QPDFObjectHandle |
| 1728 | 1748 | Objects::make_indirect(std::shared_ptr<QPDFObject> const& obj) |
| 1729 | 1749 | { |
| 1730 | 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 | 1755 | std::shared_ptr<QPDFObject> |
| ... | ... | @@ -1737,23 +1757,23 @@ Objects::get_for_parser(int id, int gen, bool parse_pdf) |
| 1737 | 1757 | { |
| 1738 | 1758 | // This method is called by the parser and therefore must not resolve any objects. |
| 1739 | 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 | 1761 | return iter->second.object; |
| 1742 | 1762 | } |
| 1743 | 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 | 1766 | if (parse_pdf) { |
| 1747 | 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 | 1772 | std::shared_ptr<QPDFObject> |
| 1753 | 1773 | Objects::get_for_json(int id, int gen) |
| 1754 | 1774 | { |
| 1755 | 1775 | auto og = QPDFObjGen(id, gen); |
| 1756 | - auto [it, inserted] = obj_cache.try_emplace(og); | |
| 1776 | + auto [it, inserted] = table.try_emplace(og); | |
| 1757 | 1777 | auto& obj = it->second.object; |
| 1758 | 1778 | if (inserted) { |
| 1759 | 1779 | obj = (xref.initialized() && !xref.type(og)) ? QPDF_Null::create(&qpdf, og) |
| ... | ... | @@ -1775,11 +1795,11 @@ Objects::replace(QPDFObjGen og, QPDFObjectHandle oh) |
| 1775 | 1795 | void |
| 1776 | 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 | 1799 | // Take care of any object handles that may be floating around. |
| 1780 | 1800 | cached->second.object->assign(QPDF_Null::create()); |
| 1781 | 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 | 1809 | // Force objects to be read from the input source if needed, then swap them in the cache. |
| 1790 | 1810 | resolve(og1); |
| 1791 | 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 | 1815 | size_t |
| 1796 | 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 | 1819 | // objects. |
| 1800 | 1820 | auto max_xref = toI(xref.size()); |
| 1801 | 1821 | if (max_xref > 0) { |
| 1802 | 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 | 1825 | auto max_id = std::numeric_limits<int>::max() - 1; |
| 1806 | 1826 | if (max_obj >= max_id || max_xref >= max_id) { |
| 1807 | 1827 | // Temporary fix. Long-term solution is |
| ... | ... | @@ -1809,7 +1829,7 @@ Objects::table_size() |
| 1809 | 1829 | // - xref table and obj cache to protect against insertion of impossibly large obj ids |
| 1810 | 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 | 1833 | return toS(++max_obj); |
| 1814 | 1834 | } |
| 1815 | 1835 | return toS(++max_xref); |
| ... | ... | @@ -1848,7 +1868,7 @@ Objects::compressible() |
| 1848 | 1868 | queue.emplace_back(trailer()); |
| 1849 | 1869 | std::vector<T> result; |
| 1850 | 1870 | if constexpr (std::is_same_v<T, QPDFObjGen>) { |
| 1851 | - result.reserve(obj_cache.size()); | |
| 1871 | + result.reserve(table.size()); | |
| 1852 | 1872 | } else if constexpr (std::is_same_v<T, bool>) { |
| 1853 | 1873 | result.resize(max_obj + 1U, false); |
| 1854 | 1874 | } else { |
| ... | ... | @@ -1872,8 +1892,8 @@ Objects::compressible() |
| 1872 | 1892 | // Check whether this is the current object. If not, remove it (which changes it into a |
| 1873 | 1893 | // direct null and therefore stops us from revisiting it) and move on to the next object |
| 1874 | 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 | 1897 | erase(og); |
| 1878 | 1898 | continue; |
| 1879 | 1899 | } | ... | ... |
libqpdf/qpdf/QPDF_objects.hh
| ... | ... | @@ -378,17 +378,7 @@ class QPDF::Objects |
| 378 | 378 | qpdf_offset_t first_item_offset_{0}; // actual value from file |
| 379 | 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 | 383 | Objects(QPDF& qpdf, QPDF::Members* m, InputSource* const& file) : |
| 394 | 384 | qpdf(qpdf), |
| ... | ... | @@ -425,12 +415,12 @@ class QPDF::Objects |
| 425 | 415 | QPDFObjectHandle |
| 426 | 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 | 419 | return {it->second.object}; |
| 430 | 420 | } else if (xref.initialized() && !xref.type(og)) { |
| 431 | 421 | return QPDF_Null::create(); |
| 432 | 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 | 424 | return {result.first->second.object}; |
| 435 | 425 | } |
| 436 | 426 | } |
| ... | ... | @@ -449,9 +439,6 @@ class QPDF::Objects |
| 449 | 439 | |
| 450 | 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 | 442 | QPDFObjectHandle read( |
| 456 | 443 | bool attempt_recovery, |
| 457 | 444 | qpdf_offset_t offset, |
| ... | ... | @@ -460,7 +447,6 @@ class QPDF::Objects |
| 460 | 447 | QPDFObjGen& og, |
| 461 | 448 | bool skip_cache_if_in_xref); |
| 462 | 449 | QPDFObject* resolve(QPDFObjGen og); |
| 463 | - void resolveObjectsInStream(int obj_stream_number); | |
| 464 | 450 | void update_table(QPDFObjGen og, std::shared_ptr<QPDFObject> const& object); |
| 465 | 451 | QPDFObjGen next_id(); |
| 466 | 452 | QPDFObjectHandle make_indirect(std::shared_ptr<QPDFObject> const& obj); |
| ... | ... | @@ -477,9 +463,23 @@ class QPDF::Objects |
| 477 | 463 | size_t table_size(); |
| 478 | 464 | |
| 479 | 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 | 478 | bool cached(QPDFObjGen og); |
| 481 | 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 | 483 | QPDFObjectHandle read_object(std::string const& description, QPDFObjGen og); |
| 484 | 484 | void read_stream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); |
| 485 | 485 | void validate_stream_line_end(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); |
| ... | ... | @@ -490,6 +490,8 @@ class QPDF::Objects |
| 490 | 490 | InputSource* const& file; |
| 491 | 491 | QPDF::Members* m; |
| 492 | 492 | Xref_table xref; |
| 493 | + | |
| 494 | + std::map<QPDFObjGen, Entry> table; | |
| 493 | 495 | }; // Objects |
| 494 | 496 | |
| 495 | 497 | #endif // QPDF_OBJECTS_HH | ... | ... |