Commit 613c1221b149d1ef8bd47fd277c8385002c281ff

Authored by m-holger
1 parent 83144b29

Refactor `PipelinePopper` to use `Pl_stack` for improved encapsulation and simpl…

…ify pipeline stack management
include/qpdf/QPDFWriter.hh
@@ -569,7 +569,7 @@ class QPDFWriter @@ -569,7 +569,7 @@ class QPDFWriter
569 // is popped. 569 // is popped.
570 570
571 void adjustAESStreamLength(size_t& length); 571 void adjustAESStreamLength(size_t& length);
572 - void pushEncryptionFilter(PipelinePopper&); 572 + PipelinePopper pushEncryptionFilter();
573 void pushMD5Pipeline(PipelinePopper&); 573 void pushMD5Pipeline(PipelinePopper&);
574 void computeDeterministicIDData(); 574 void computeDeterministicIDData();
575 575
libqpdf/QPDFWriter.cc
@@ -50,22 +50,52 @@ QPDFWriter::FunctionProgressReporter::reportProgress(int progress) @@ -50,22 +50,52 @@ QPDFWriter::FunctionProgressReporter::reportProgress(int progress)
50 handler(progress); 50 handler(progress);
51 } 51 }
52 52
53 -// An reference to a PipelinePopper instance is passed into activatePipelineStack. When the  
54 -// PipelinePopper goes out of scope, the pipeline stack is popped. PipelinePopper's destructor  
55 -// calls finish on the current pipeline and pops the pipeline stack until the top of stack is a  
56 -// previous active top of stack, and restores the pipeline to that point. It deletes any  
57 -// pipelines that it pops. If the bp argument is non-null and any of the stack items are of type  
58 -// Pl_Buffer, the buffer is retrieved. 53 +namespace
  54 +{
  55 + class Pl_stack;
  56 +}
  57 +
  58 +// A PipelinePopper is normally returned by Pl_stack::activate, or, if necessary, a reference to a
  59 +// PipelinePopper instance can be passed into activate. When the PipelinePopper goes out of scope,
  60 +// the pipeline stack is popped. This causes finish to be called on the current pipeline and the
  61 +// pipeline stack to be popped until the top of stack is a previous active top of stack, and
  62 +// restores the pipeline to that point. It deletes any pipelines that it pops.
59 class QPDFWriter::PipelinePopper 63 class QPDFWriter::PipelinePopper
60 { 64 {
61 public: 65 public:
62 - PipelinePopper(QPDFWriter* qw) :  
63 - qw(qw) 66 + PipelinePopper(Pl_stack& stack) :
  67 + stack(&stack)
64 { 68 {
65 } 69 }
  70 + PipelinePopper() = default;
  71 + PipelinePopper(PipelinePopper const&) = delete;
  72 + PipelinePopper(PipelinePopper&& other) noexcept
  73 + {
  74 + // For MSVC, default pops the stack
  75 + if (this != &other) {
  76 + stack = other.stack;
  77 + stack_id = other.stack_id;
  78 + other.stack = nullptr;
  79 + other.stack_id = 0;
  80 + };
  81 + }
  82 + PipelinePopper& operator=(PipelinePopper const&) = delete;
  83 + PipelinePopper&
  84 + operator=(PipelinePopper&& other) noexcept
  85 + {
  86 + // For MSVC, default pops the stack
  87 + if (this != &other) {
  88 + stack = other.stack;
  89 + stack_id = other.stack_id;
  90 + other.stack = nullptr;
  91 + other.stack_id = 0;
  92 + };
  93 + return *this;
  94 + }
  95 +
66 ~PipelinePopper(); 96 ~PipelinePopper();
67 97
68 - QPDFWriter* qw{nullptr}; 98 + Pl_stack* stack{nullptr};
69 unsigned long stack_id{0}; 99 unsigned long stack_id{0};
70 }; 100 };
71 101
@@ -82,6 +112,12 @@ namespace @@ -82,6 +112,12 @@ namespace
82 { 112 {
83 } 113 }
84 114
  115 + PP
  116 + popper()
  117 + {
  118 + return {*this};
  119 + }
  120 +
85 void 121 void
86 initialize(Pipeline* p) 122 initialize(Pipeline* p)
87 { 123 {
@@ -90,6 +126,14 @@ namespace @@ -90,6 +126,14 @@ namespace
90 stack.emplace_back(top); 126 stack.emplace_back(top);
91 } 127 }
92 128
  129 + PP
  130 + activate(std::string& str)
  131 + {
  132 + PP pp{*this};
  133 + activate(pp, str);
  134 + return pp;
  135 + }
  136 +
93 void 137 void
94 activate(PP& pp, std::string& str) 138 activate(PP& pp, std::string& str)
95 { 139 {
@@ -103,6 +147,17 @@ namespace @@ -103,6 +147,17 @@ namespace
103 activate(pp, false, &count_buffer, std::move(link)); 147 activate(pp, false, &count_buffer, std::move(link));
104 } 148 }
105 149
  150 + PP
  151 + activate(
  152 + bool discard = false,
  153 + std::string* str = nullptr,
  154 + std::unique_ptr<pl::Link> link = nullptr)
  155 + {
  156 + PP pp{*this};
  157 + activate(pp, discard, str, std::move(link));
  158 + return pp;
  159 + }
  160 +
106 void 161 void
107 activate( 162 activate(
108 PP& pp, 163 PP& pp,
@@ -178,6 +233,13 @@ namespace @@ -178,6 +233,13 @@ namespace
178 }; 233 };
179 } // namespace 234 } // namespace
180 235
  236 +QPDFWriter::PipelinePopper::~PipelinePopper()
  237 +{
  238 + if (stack) {
  239 + stack->pop(stack_id);
  240 + }
  241 +}
  242 +
181 class QPDFWriter::Members 243 class QPDFWriter::Members
182 { 244 {
183 friend class QPDFWriter; 245 friend class QPDFWriter;
@@ -1014,11 +1076,6 @@ QPDFWriter::write_no_qdf(Args&amp;&amp;... args) @@ -1014,11 +1076,6 @@ QPDFWriter::write_no_qdf(Args&amp;&amp;... args)
1014 return *this; 1076 return *this;
1015 } 1077 }
1016 1078
1017 -QPDFWriter::PipelinePopper::~PipelinePopper()  
1018 -{  
1019 - qw->m->pipeline_stack.pop(stack_id);  
1020 -}  
1021 -  
1022 void 1079 void
1023 QPDFWriter::adjustAESStreamLength(size_t& length) 1080 QPDFWriter::adjustAESStreamLength(size_t& length)
1024 { 1081 {
@@ -1029,9 +1086,10 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length) @@ -1029,9 +1086,10 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length)
1029 } 1086 }
1030 } 1087 }
1031 1088
1032 -void  
1033 -QPDFWriter::pushEncryptionFilter(PipelinePopper& pp) 1089 +QPDFWriter::PipelinePopper
  1090 +QPDFWriter::pushEncryptionFilter()
1034 { 1091 {
  1092 + auto pp = m->pipeline_stack.popper();
1035 if (m->encryption && !m->cur_data_key.empty()) { 1093 if (m->encryption && !m->cur_data_key.empty()) {
1036 Pipeline* p = nullptr; 1094 Pipeline* p = nullptr;
1037 if (m->encrypt_use_aes) { 1095 if (m->encrypt_use_aes) {
@@ -1053,6 +1111,7 @@ QPDFWriter::pushEncryptionFilter(PipelinePopper&amp; pp) @@ -1053,6 +1111,7 @@ QPDFWriter::pushEncryptionFilter(PipelinePopper&amp; pp)
1053 // Must call this unconditionally so we can call popPipelineStack to balance 1111 // Must call this unconditionally so we can call popPipelineStack to balance
1054 // pushEncryptionFilter(). 1112 // pushEncryptionFilter().
1055 m->pipeline_stack.activate(pp); 1113 m->pipeline_stack.activate(pp);
  1114 + return pp;
1056 } 1115 }
1057 1116
1058 void 1117 void
@@ -1328,12 +1387,9 @@ QPDFWriter::willFilterStream( @@ -1328,12 +1387,9 @@ QPDFWriter::willFilterStream(
1328 1387
1329 bool filtered = false; 1388 bool filtered = false;
1330 for (bool first_attempt: {true, false}) { 1389 for (bool first_attempt: {true, false}) {
1331 - PipelinePopper pp_stream_data(this);  
1332 - if (stream_data != nullptr) {  
1333 - m->pipeline_stack.activate(pp_stream_data, *stream_data);  
1334 - } else {  
1335 - m->pipeline_stack.activate(pp_stream_data, true);  
1336 - } 1390 + auto pp_stream_data = stream_data ? m->pipeline_stack.activate(*stream_data)
  1391 + : m->pipeline_stack.activate(true);
  1392 +
1337 try { 1393 try {
1338 filtered = stream.pipeStreamData( 1394 filtered = stream.pipeStreamData(
1339 m->pipeline, 1395 m->pipeline,
@@ -1593,8 +1649,7 @@ QPDFWriter::unparseObject( @@ -1593,8 +1649,7 @@ QPDFWriter::unparseObject(
1593 char last_char = stream_data.empty() ? '\0' : stream_data.back(); 1649 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1594 write("\nstream\n"); 1650 write("\nstream\n");
1595 { 1651 {
1596 - PipelinePopper pp_enc(this);  
1597 - pushEncryptionFilter(pp_enc); 1652 + auto pp_enc = pushEncryptionFilter();
1598 write(stream_data); 1653 write(stream_data);
1599 } 1654 }
1600 1655
@@ -1683,8 +1738,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1683,8 +1738,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1683 const bool compressed = m->compress_streams && !m->qdf_mode; 1738 const bool compressed = m->compress_streams && !m->qdf_mode;
1684 { 1739 {
1685 // Pass 1 1740 // Pass 1
1686 - PipelinePopper pp_ostream_pass1(this);  
1687 - m->pipeline_stack.activate(pp_ostream_pass1, stream_buffer_pass1); 1741 + auto pp_ostream_pass1 = m->pipeline_stack.activate(stream_buffer_pass1);
1688 1742
1689 int count = -1; 1743 int count = -1;
1690 for (auto const& obj: m->object_stream_to_objects[old_id]) { 1744 for (auto const& obj: m->object_stream_to_objects[old_id]) {
@@ -1726,7 +1780,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1726,7 +1780,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1726 } 1780 }
1727 } 1781 }
1728 { 1782 {
1729 - PipelinePopper pp_ostream(this); 1783 + auto pp_ostream = m->pipeline_stack.popper();
1730 // Adjust offsets to skip over comment before first object 1784 // Adjust offsets to skip over comment before first object
1731 first = offsets.at(0); 1785 first = offsets.at(0);
1732 for (auto& iter: offsets) { 1786 for (auto& iter: offsets) {
@@ -1735,8 +1789,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1735,8 +1789,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1735 1789
1736 // Take one pass at writing pairs of numbers so we can get their size information 1790 // Take one pass at writing pairs of numbers so we can get their size information
1737 { 1791 {
1738 - PipelinePopper pp_discard(this);  
1739 - m->pipeline_stack.activate(pp_discard, true); 1792 + auto pp_discard = m->pipeline_stack.activate(true);
1740 writeObjectStreamOffsets(offsets, first_obj); 1793 writeObjectStreamOffsets(offsets, first_obj);
1741 first += m->pipeline->getCount(); 1794 first += m->pipeline->getCount();
1742 } 1795 }
@@ -1782,8 +1835,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1782,8 +1835,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1782 QTC::TC("qpdf", "QPDFWriter encrypt object stream"); 1835 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1783 } 1836 }
1784 { 1837 {
1785 - PipelinePopper pp_enc(this);  
1786 - pushEncryptionFilter(pp_enc); 1838 + auto pp_enc = pushEncryptionFilter();
1787 write(stream_buffer_pass2); 1839 write(stream_buffer_pass2);
1788 } 1840 }
1789 if (m->newline_before_endstream) { 1841 if (m->newline_before_endstream) {
@@ -2405,8 +2457,7 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2405,8 +2457,7 @@ QPDFWriter::writeHintStream(int hint_id)
2405 } 2457 }
2406 char last_char = hint_buffer.empty() ? '\0' : hint_buffer.back(); 2458 char last_char = hint_buffer.empty() ? '\0' : hint_buffer.back();
2407 { 2459 {
2408 - PipelinePopper pp_enc(this);  
2409 - pushEncryptionFilter(pp_enc); 2460 + auto pp_enc = pushEncryptionFilter();
2410 write(hint_buffer); 2461 write(hint_buffer);
2411 } 2462 }
2412 2463
@@ -2504,7 +2555,7 @@ QPDFWriter::writeXRefStream( @@ -2504,7 +2555,7 @@ QPDFWriter::writeXRefStream(
2504 std::string xref_data; 2555 std::string xref_data;
2505 const bool compressed = m->compress_streams && !m->qdf_mode; 2556 const bool compressed = m->compress_streams && !m->qdf_mode;
2506 { 2557 {
2507 - PipelinePopper pp_xref(this); 2558 + auto pp_xref = m->pipeline_stack.popper();
2508 if (compressed) { 2559 if (compressed) {
2509 m->pipeline_stack.clear_buffer(); 2560 m->pipeline_stack.clear_buffer();
2510 auto link = pl::create<pl::String>(xref_data); 2561 auto link = pl::create<pl::String>(xref_data);
@@ -2711,8 +2762,8 @@ QPDFWriter::writeLinearized() @@ -2711,8 +2762,8 @@ QPDFWriter::writeLinearized()
2711 // Write file in two passes. Part numbers refer to PDF spec 1.4. 2762 // Write file in two passes. Part numbers refer to PDF spec 1.4.
2712 2763
2713 FILE* lin_pass1_file = nullptr; 2764 FILE* lin_pass1_file = nullptr;
2714 - auto pp_pass1 = std::make_unique<PipelinePopper>(this);  
2715 - auto pp_md5 = std::make_unique<PipelinePopper>(this); 2765 + auto pp_pass1 = std::make_unique<PipelinePopper>(m->pipeline_stack);
  2766 + auto pp_md5 = std::make_unique<PipelinePopper>(m->pipeline_stack);
2716 for (int pass: {1, 2}) { 2767 for (int pass: {1, 2}) {
2717 if (pass == 1) { 2768 if (pass == 1) {
2718 if (!m->lin_pass1_filename.empty()) { 2769 if (!m->lin_pass1_filename.empty()) {
@@ -2911,8 +2962,7 @@ QPDFWriter::writeLinearized() @@ -2911,8 +2962,7 @@ QPDFWriter::writeLinearized()
2911 2962
2912 // Write hint stream to a buffer 2963 // Write hint stream to a buffer
2913 { 2964 {
2914 - PipelinePopper pp_hint(this);  
2915 - m->pipeline_stack.activate(pp_hint, hint_buffer); 2965 + auto pp_hint = m->pipeline_stack.activate(hint_buffer);
2916 writeHintStream(hint_id); 2966 writeHintStream(hint_id);
2917 } 2967 }
2918 hint_length = QIntC::to_offset(hint_buffer.size()); 2968 hint_length = QIntC::to_offset(hint_buffer.size());
@@ -3030,7 +3080,7 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr) @@ -3030,7 +3080,7 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr)
3030 void 3080 void
3031 QPDFWriter::writeStandard() 3081 QPDFWriter::writeStandard()
3032 { 3082 {
3033 - auto pp_md5 = PipelinePopper(this); 3083 + auto pp_md5 = m->pipeline_stack.popper();
3034 if (m->deterministic_id) { 3084 if (m->deterministic_id) {
3035 pushMD5Pipeline(pp_md5); 3085 pushMD5Pipeline(pp_md5);
3036 } 3086 }