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 569 // is popped.
570 570  
571 571 void adjustAESStreamLength(size_t& length);
572   - void pushEncryptionFilter(PipelinePopper&);
  572 + PipelinePopper pushEncryptionFilter();
573 573 void pushMD5Pipeline(PipelinePopper&);
574 574 void computeDeterministicIDData();
575 575  
... ...
libqpdf/QPDFWriter.cc
... ... @@ -50,22 +50,52 @@ QPDFWriter::FunctionProgressReporter::reportProgress(int progress)
50 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 63 class QPDFWriter::PipelinePopper
60 64 {
61 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 96 ~PipelinePopper();
67 97  
68   - QPDFWriter* qw{nullptr};
  98 + Pl_stack* stack{nullptr};
69 99 unsigned long stack_id{0};
70 100 };
71 101  
... ... @@ -82,6 +112,12 @@ namespace
82 112 {
83 113 }
84 114  
  115 + PP
  116 + popper()
  117 + {
  118 + return {*this};
  119 + }
  120 +
85 121 void
86 122 initialize(Pipeline* p)
87 123 {
... ... @@ -90,6 +126,14 @@ namespace
90 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 137 void
94 138 activate(PP& pp, std::string& str)
95 139 {
... ... @@ -103,6 +147,17 @@ namespace
103 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 161 void
107 162 activate(
108 163 PP& pp,
... ... @@ -178,6 +233,13 @@ namespace
178 233 };
179 234 } // namespace
180 235  
  236 +QPDFWriter::PipelinePopper::~PipelinePopper()
  237 +{
  238 + if (stack) {
  239 + stack->pop(stack_id);
  240 + }
  241 +}
  242 +
181 243 class QPDFWriter::Members
182 244 {
183 245 friend class QPDFWriter;
... ... @@ -1014,11 +1076,6 @@ QPDFWriter::write_no_qdf(Args&amp;&amp;... args)
1014 1076 return *this;
1015 1077 }
1016 1078  
1017   -QPDFWriter::PipelinePopper::~PipelinePopper()
1018   -{
1019   - qw->m->pipeline_stack.pop(stack_id);
1020   -}
1021   -
1022 1079 void
1023 1080 QPDFWriter::adjustAESStreamLength(size_t& length)
1024 1081 {
... ... @@ -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 1093 if (m->encryption && !m->cur_data_key.empty()) {
1036 1094 Pipeline* p = nullptr;
1037 1095 if (m->encrypt_use_aes) {
... ... @@ -1053,6 +1111,7 @@ QPDFWriter::pushEncryptionFilter(PipelinePopper&amp; pp)
1053 1111 // Must call this unconditionally so we can call popPipelineStack to balance
1054 1112 // pushEncryptionFilter().
1055 1113 m->pipeline_stack.activate(pp);
  1114 + return pp;
1056 1115 }
1057 1116  
1058 1117 void
... ... @@ -1328,12 +1387,9 @@ QPDFWriter::willFilterStream(
1328 1387  
1329 1388 bool filtered = false;
1330 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 1393 try {
1338 1394 filtered = stream.pipeStreamData(
1339 1395 m->pipeline,
... ... @@ -1593,8 +1649,7 @@ QPDFWriter::unparseObject(
1593 1649 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1594 1650 write("\nstream\n");
1595 1651 {
1596   - PipelinePopper pp_enc(this);
1597   - pushEncryptionFilter(pp_enc);
  1652 + auto pp_enc = pushEncryptionFilter();
1598 1653 write(stream_data);
1599 1654 }
1600 1655  
... ... @@ -1683,8 +1738,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1683 1738 const bool compressed = m->compress_streams && !m->qdf_mode;
1684 1739 {
1685 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 1743 int count = -1;
1690 1744 for (auto const& obj: m->object_stream_to_objects[old_id]) {
... ... @@ -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 1784 // Adjust offsets to skip over comment before first object
1731 1785 first = offsets.at(0);
1732 1786 for (auto& iter: offsets) {
... ... @@ -1735,8 +1789,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1735 1789  
1736 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 1793 writeObjectStreamOffsets(offsets, first_obj);
1741 1794 first += m->pipeline->getCount();
1742 1795 }
... ... @@ -1782,8 +1835,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1782 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 1839 write(stream_buffer_pass2);
1788 1840 }
1789 1841 if (m->newline_before_endstream) {
... ... @@ -2405,8 +2457,7 @@ QPDFWriter::writeHintStream(int hint_id)
2405 2457 }
2406 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 2461 write(hint_buffer);
2411 2462 }
2412 2463  
... ... @@ -2504,7 +2555,7 @@ QPDFWriter::writeXRefStream(
2504 2555 std::string xref_data;
2505 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 2559 if (compressed) {
2509 2560 m->pipeline_stack.clear_buffer();
2510 2561 auto link = pl::create<pl::String>(xref_data);
... ... @@ -2711,8 +2762,8 @@ QPDFWriter::writeLinearized()
2711 2762 // Write file in two passes. Part numbers refer to PDF spec 1.4.
2712 2763  
2713 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 2767 for (int pass: {1, 2}) {
2717 2768 if (pass == 1) {
2718 2769 if (!m->lin_pass1_filename.empty()) {
... ... @@ -2911,8 +2962,7 @@ QPDFWriter::writeLinearized()
2911 2962  
2912 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 2966 writeHintStream(hint_id);
2917 2967 }
2918 2968 hint_length = QIntC::to_offset(hint_buffer.size());
... ... @@ -3030,7 +3080,7 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr)
3030 3080 void
3031 3081 QPDFWriter::writeStandard()
3032 3082 {
3033   - auto pp_md5 = PipelinePopper(this);
  3083 + auto pp_md5 = m->pipeline_stack.popper();
3034 3084 if (m->deterministic_id) {
3035 3085 pushMD5Pipeline(pp_md5);
3036 3086 }
... ...