Commit 16cb3ca5c55c3288ffec8e4b641f3531d4da7afd

Authored by m-holger
1 parent 9e8bd681

Refactor error handling across codebase with `util::assertion` and `util::no_ci_rt_error_if`.

- Standardize error handling by replacing repetitive throw statements with helper functions.
- Add test coverage for new helper functions in error handling.
- Enhance maintainability and readability with concise utilities.
libqpdf/NNTree.cc
... ... @@ -9,6 +9,7 @@
9 9 #include <qpdf/QPDF_private.hh>
10 10 #include <qpdf/QTC.hh>
11 11 #include <qpdf/QUtil.hh>
  12 +#include <qpdf/Util.hh>
12 13  
13 14 #include <bit>
14 15 #include <exception>
... ... @@ -65,10 +66,8 @@ NNTreeIterator::updateIValue(bool allow_invalid)
65 66 }
66 67  
67 68 if (item_number < 0 || !node) {
68   - if (!allow_invalid) {
69   - throw std::logic_error(
70   - "attempt made to dereference an invalid name/number tree iterator");
71   - }
  69 + util::assertion(
  70 + allow_invalid, "attempt made to dereference an invalid name/number tree iterator");
72 71 return;
73 72 }
74 73 impl.error(node, "update ivalue: items array is too short");
... ... @@ -210,9 +209,7 @@ NNTreeIterator::split(Dictionary to_split, std::list&lt;PathElement&gt;::iterator pare
210 209 // node: A
211 210 // item_number: 0
212 211  
213   - if (!valid()) {
214   - throw std::logic_error("NNTreeIterator::split called an invalid iterator");
215   - }
  212 + util::assertion(valid(), "NNTreeIterator::split called an invalid iterator");
216 213  
217 214 // Find the array we actually need to split, which is either this node's kids or items.
218 215 Array kids = to_split["/Kids"];
... ... @@ -228,13 +225,12 @@ NNTreeIterator::split(Dictionary to_split, std::list&lt;PathElement&gt;::iterator pare
228 225 first_half = kids;
229 226 n = nkids;
230 227 key = "/Kids";
231   - } else if (nitems > 0) {
  228 + } else {
  229 + util::assertion(nitems > 0, "NNTreeIterator::split called on invalid node");
232 230 first_half = items;
233 231 n = nitems;
234 232 threshold *= 2;
235 233 key = impl.itemsKey();
236   - } else {
237   - throw std::logic_error("NNTreeIterator::split called on invalid node");
238 234 }
239 235  
240 236 if (n <= threshold) {
... ... @@ -369,9 +365,7 @@ NNTreeIterator::remove()
369 365 {
370 366 // Remove this item, leaving the tree valid and this iterator pointing to the next item.
371 367  
372   - if (!valid()) {
373   - throw std::logic_error("attempt made to remove an invalid iterator");
374   - }
  368 + util::assertion(valid(), "attempt made to remove an invalid iterator");
375 369 Array items = node[impl.itemsKey()];
376 370 int nitems = static_cast<int>(items.size());
377 371 if (std::cmp_greater(item_number + 2, nitems)) {
... ... @@ -396,13 +390,12 @@ NNTreeIterator::remove()
396 390 // the previous item.
397 391 item_number -= 2;
398 392 increment(false);
399   - } else if (item_number < nitems) {
  393 + } else {
  394 + util::assertion(
  395 + item_number < nitems, "NNTreeIterator::remove: item_number > nitems after erase");
400 396 // We don't have to do anything since the removed item's successor now occupies its
401 397 // former location.
402 398 updateIValue();
403   - } else {
404   - // We already checked to ensure this condition would not happen.
405   - throw std::logic_error("NNTreeIterator::remove: item_number > nitems after erase");
406 399 }
407 400 return;
408 401 }
... ...
libqpdf/Pl_AES_PDF.cc
... ... @@ -3,10 +3,13 @@
3 3 #include <qpdf/QIntC.hh>
4 4 #include <qpdf/QPDFCryptoProvider.hh>
5 5 #include <qpdf/QUtil.hh>
  6 +#include <qpdf/Util.hh>
  7 +
6 8 #include <cstring>
7   -#include <stdexcept>
8 9 #include <string>
9 10  
  11 +using namespace qpdf;
  12 +
10 13 bool Pl_AES_PDF::use_static_iv = false;
11 14  
12 15 Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, bool encrypt, std::string key) :
... ... @@ -15,12 +18,8 @@ Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, bool encrypt, std
15 18 crypto(QPDFCryptoProvider::getImpl()),
16 19 encrypt(encrypt)
17 20 {
18   - if (!next) {
19   - throw std::logic_error("Attempt to create Pl_AES_PDF with nullptr as next");
20   - }
21   - if (!(key.size() == 32 || key.size() == 16)) {
22   - throw std::runtime_error("unsupported key length");
23   - }
  21 + util::assertion(next, "Attempt to create Pl_AES_PDF with nullptr as next");
  22 + util::no_ci_rt_error_if(!(key.size() == 32 || key.size() == 16), "unsupported key length");
24 23 std::memset(this->inbuf, 0, this->buf_size);
25 24 std::memset(this->outbuf, 0, this->buf_size);
26 25 std::memset(this->cbc_block, 0, this->buf_size);
... ... @@ -41,12 +40,10 @@ Pl_AES_PDF::disablePadding()
41 40 void
42 41 Pl_AES_PDF::setIV(unsigned char const* iv, size_t bytes)
43 42 {
44   - if (bytes != buf_size) {
45   - throw std::logic_error(
46   - "Pl_AES_PDF: specified initialization vector"
47   - " size in bytes must be " +
  43 + util::assertion(
  44 + bytes == buf_size,
  45 + "Pl_AES_PDF: specified initialization vector size in bytes must be " +
48 46 std::to_string(bytes));
49   - }
50 47 use_specified_iv = true;
51 48 memcpy(specified_iv, iv, bytes);
52 49 }
... ... @@ -103,9 +100,7 @@ Pl_AES_PDF::finish()
103 100 // This is never supposed to happen as the output is always supposed to be padded.
104 101 // However, we have encountered files for which the output is not a multiple of the
105 102 // block size. In this case, pad with zeroes and hope for the best.
106   - if (offset >= buf_size) {
107   - throw std::logic_error("buffer overflow in AES encryption pipeline");
108   - }
  103 + util::assertion(offset < buf_size, "buffer overflow in AES encryption pipeline");
109 104 std::memset(inbuf + offset, 0, buf_size - offset);
110 105 offset = buf_size;
111 106 }
... ... @@ -136,9 +131,7 @@ Pl_AES_PDF::initializeVector()
136 131 void
137 132 Pl_AES_PDF::flush(bool strip_padding)
138 133 {
139   - if (offset != buf_size) {
140   - throw std::logic_error("AES pipeline: flush called when buffer was not full");
141   - }
  134 + util::assertion(offset == buf_size, "AES pipeline: flush called when buffer was not full");
142 135  
143 136 if (first) {
144 137 first = false;
... ...
libqpdf/Pl_ASCII85Decoder.cc
1 1 #include <qpdf/Pl_ASCII85Decoder.hh>
2 2  
3 3 #include <qpdf/QTC.hh>
  4 +#include <qpdf/Util.hh>
  5 +
4 6 #include <cstring>
5 7 #include <stdexcept>
6 8  
  9 +using namespace qpdf;
  10 +
7 11 Pl_ASCII85Decoder::Pl_ASCII85Decoder(char const* identifier, Pipeline* next) :
8 12 Pipeline(identifier, next)
9 13 {
10   - if (!next) {
11   - throw std::logic_error("Attempt to create Pl_ASCII85Decoder with nullptr as next");
12   - }
  14 + util::assertion(next, "Attempt to create Pl_ASCII85Decoder with nullptr as next");
13 15 }
14 16  
15 17 void
... ... @@ -33,12 +35,9 @@ Pl_ASCII85Decoder::write(unsigned char const* buf, size_t len)
33 35 if (eod > 1) {
34 36 break;
35 37 } else if (eod == 1) {
36   - if (buf[i] == '>') {
37   - flush();
38   - eod = 2;
39   - } else {
40   - throw std::runtime_error("broken end-of-data sequence in base 85 data");
41   - }
  38 + util::no_ci_rt_error_if(buf[i] != '>', "broken end-of-data sequence in base 85 data");
  39 + flush();
  40 + eod = 2;
42 41 } else {
43 42 switch (buf[i]) {
44 43 case '~':
... ... @@ -48,12 +47,10 @@ Pl_ASCII85Decoder::write(unsigned char const* buf, size_t len)
48 47 case 'z':
49 48 if (pos != 0) {
50 49 throw std::runtime_error("unexpected z during base 85 decode");
51   - } else {
52   - QTC::TC("libtests", "Pl_ASCII85Decoder read z");
53   - unsigned char zeroes[4];
54   - memset(zeroes, '\0', 4);
55   - next()->write(zeroes, 4);
56 50 }
  51 + unsigned char zeroes[4];
  52 + memset(zeroes, '\0', 4);
  53 + next()->write(zeroes, 4);
57 54 break;
58 55  
59 56 default:
... ... @@ -76,7 +73,6 @@ void
76 73 Pl_ASCII85Decoder::flush()
77 74 {
78 75 if (this->pos == 0) {
79   - QTC::TC("libtests", "Pl_ASCII85Decoder no-op flush");
80 76 return;
81 77 }
82 78 unsigned long lval = 0;
... ...
libqpdf/Pl_ASCIIHexDecoder.cc
1 1 #include <qpdf/Pl_ASCIIHexDecoder.hh>
2 2  
3 3 #include <qpdf/QTC.hh>
  4 +#include <qpdf/Util.hh>
  5 +
4 6 #include <cctype>
5 7 #include <stdexcept>
6 8  
  9 +using namespace qpdf;
7 10 using namespace std::literals;
8 11  
9 12 Pl_ASCIIHexDecoder::Pl_ASCIIHexDecoder(char const* identifier, Pipeline* next) :
10 13 Pipeline(identifier, next)
11 14 {
12   - if (!next) {
13   - throw std::logic_error("Attempt to create Pl_ASCIIHexDecoder with nullptr as next");
14   - }
  15 + util::assertion(next, "Attempt to create Pl_ASCIIHexDecoder with nullptr as next");
15 16 }
16 17  
17 18 void
... ...
libqpdf/Pl_Buffer.cc
1 1 #include <qpdf/Pl_Buffer.hh>
2 2  
  3 +#include <qpdf/Util.hh>
  4 +
3 5 #include <algorithm>
4 6 #include <cstdlib>
5 7 #include <cstring>
6 8 #include <stdexcept>
7 9  
  10 +using namespace qpdf;
  11 +
8 12 class Pl_Buffer::Members
9 13 {
10 14 public:
... ... @@ -50,9 +54,7 @@ Pl_Buffer::finish()
50 54 Buffer*
51 55 Pl_Buffer::getBuffer()
52 56 {
53   - if (!m->ready) {
54   - throw std::logic_error("Pl_Buffer::getBuffer() called when not ready");
55   - }
  57 + util::assertion(m->ready, "Pl_Buffer::getBuffer() called when not ready");
56 58 auto* b = new Buffer(std::move(m->data));
57 59 m->data.clear();
58 60 return b;
... ... @@ -61,9 +63,7 @@ Pl_Buffer::getBuffer()
61 63 std::string
62 64 Pl_Buffer::getString()
63 65 {
64   - if (!m->ready) {
65   - throw std::logic_error("Pl_Buffer::getString() called when not ready");
66   - }
  66 + util::assertion(m->ready, "Pl_Buffer::getString() called when not ready");
67 67 auto s = std::move(m->data);
68 68 m->data.clear();
69 69 return s;
... ... @@ -78,9 +78,7 @@ Pl_Buffer::getBufferSharedPointer()
78 78 void
79 79 Pl_Buffer::getMallocBuffer(unsigned char** buf, size_t* len)
80 80 {
81   - if (!m->ready) {
82   - throw std::logic_error("Pl_Buffer::getMallocBuffer() called when not ready");
83   - }
  81 + util::assertion(m->ready, "Pl_Buffer::getMallocBuffer() called when not ready");
84 82 auto size = m->data.size();
85 83 *len = size;
86 84 if (size > 0) {
... ...
libqpdf/Pl_Concatenate.cc
1 1 #include <qpdf/Pl_Concatenate.hh>
2 2  
3   -#include <stdexcept>
  3 +#include <qpdf/Util.hh>
  4 +
  5 +using namespace qpdf;
4 6  
5 7 Pl_Concatenate::Pl_Concatenate(char const* identifier, Pipeline* next) :
6 8 Pipeline(identifier, next)
7 9 {
8   - if (!next) {
9   - throw std::logic_error("Attempt to create Pl_Concatenate with nullptr as next");
10   - }
  10 + util::assertion(next, "Attempt to create Pl_Concatenate with nullptr as next");
11 11 }
12 12  
13 13 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
... ...
libqpdf/Pl_Count.cc
1 1 #include <qpdf/Pl_Count.hh>
2 2  
3 3 #include <qpdf/QIntC.hh>
  4 +#include <qpdf/Util.hh>
  5 +
  6 +using namespace qpdf;
4 7  
5 8 class Pl_Count::Members
6 9 {
... ... @@ -18,15 +21,11 @@ Pl_Count::Pl_Count(char const* identifier, Pipeline* next) :
18 21 Pipeline(identifier, next),
19 22 m(std::make_unique<Members>())
20 23 {
21   - if (!next) {
22   - throw std::logic_error("Attempt to create Pl_Count with nullptr as next");
23   - }
  24 + util::assertion(next, "Attempt to create Pl_Count with nullptr as next");
24 25 }
25 26  
26   -Pl_Count::~Pl_Count() // NOLINT (modernize-use-equals-default)
27   -{
28   - // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
29   -}
  27 +Pl_Count::~Pl_Count() = default;
  28 +// Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
30 29  
31 30 void
32 31 Pl_Count::write(unsigned char const* buf, size_t len)
... ...
libqpdf/Pl_DCT.cc
... ... @@ -2,6 +2,7 @@
2 2  
3 3 #include <qpdf/QIntC.hh>
4 4 #include <qpdf/QTC.hh>
  5 +#include <qpdf/Util.hh>
5 6  
6 7 #include <csetjmp>
7 8 #include <stdexcept>
... ... @@ -11,6 +12,8 @@
11 12 # error "qpdf does not support libjpeg built with BITS_IN_JSAMPLE != 8"
12 13 #endif
13 14  
  15 +using namespace qpdf;
  16 +
14 17 namespace
15 18 {
16 19 class FunctionCallbackConfig: public Pl_DCT::CompressConfig
... ... @@ -118,9 +121,7 @@ Pl_DCT::Pl_DCT(char const* identifier, Pipeline* next) :
118 121 Pipeline(identifier, next),
119 122 m(std::make_unique<Members>())
120 123 {
121   - if (!next) {
122   - throw std::logic_error("Attempt to create Pl_DCT with nullptr as next");
123   - }
  124 + util::assertion(next, "Attempt to create Pl_DCT with nullptr as next");
124 125 }
125 126  
126 127 void
... ... @@ -285,12 +286,10 @@ fill_buffer_input_buffer(j_decompress_ptr)
285 286 static void
286 287 skip_buffer_input_data(j_decompress_ptr cinfo, long num_bytes)
287 288 {
288   - if (num_bytes < 0) {
289   - throw std::runtime_error(
290   - "reading jpeg: jpeg library requested skipping a negative number of bytes");
291   - }
  289 + util::no_ci_rt_error_if(
  290 + num_bytes < 0, "reading jpeg: jpeg library requested skipping a negative number of bytes");
292 291 size_t to_skip = QIntC::to_size(num_bytes);
293   - if ((to_skip > 0) && (to_skip <= cinfo->src->bytes_in_buffer)) {
  292 + if (to_skip > 0 && to_skip <= cinfo->src->bytes_in_buffer) {
294 293 cinfo->src->next_input_byte += to_skip;
295 294 cinfo->src->bytes_in_buffer -= to_skip;
296 295 } else if (to_skip != 0) {
... ... @@ -354,11 +353,10 @@ Pl_DCT::compress(void* cinfo_p)
354 353 unsigned int width = cinfo->image_width * QIntC::to_uint(cinfo->input_components);
355 354 size_t expected_size = QIntC::to_size(cinfo->image_height) *
356 355 QIntC::to_size(cinfo->image_width) * QIntC::to_size(cinfo->input_components);
357   - if (m->buf.size() != expected_size) {
358   - throw std::runtime_error(
359   - "Pl_DCT: image buffer size = " + std::to_string(m->buf.size()) +
  356 + util::no_ci_rt_error_if(
  357 + m->buf.size() != expected_size,
  358 + "Pl_DCT: image buffer size = " + std::to_string(m->buf.size()) +
360 359 "; expected size = " + std::to_string(expected_size));
361   - }
362 360 JSAMPROW row_pointer[1];
363 361 auto buffer = reinterpret_cast<unsigned char*>(m->buf.data());
364 362 while (cinfo->next_scanline < cinfo->image_height) {
... ...
libqpdf/Pl_Flate.cc
... ... @@ -6,12 +6,15 @@
6 6  
7 7 #include <qpdf/QIntC.hh>
8 8 #include <qpdf/QUtil.hh>
  9 +#include <qpdf/Util.hh>
9 10 #include <qpdf/qpdf-config.h>
10 11  
11 12 #ifdef ZOPFLI
12 13 # include <zopfli.h>
13 14 #endif
14 15  
  16 +using namespace qpdf;
  17 +
15 18 namespace
16 19 {
17 20 unsigned long long memory_limit_{0};
... ... @@ -31,10 +34,9 @@ Pl_Flate::Members::Members(size_t out_bufsize, action_e action) :
31 34 // development files available, which particularly helps in a Windows environment.
32 35 zdata = new z_stream;
33 36  
34   - if (out_bufsize > UINT_MAX) {
35   - throw std::runtime_error(
36   - "Pl_Flate: zlib doesn't support buffer sizes larger than unsigned int");
37   - }
  37 + util::no_ci_rt_error_if(
  38 + out_bufsize > UINT_MAX,
  39 + "Pl_Flate: zlib doesn't support buffer sizes larger than unsigned int");
38 40  
39 41 z_stream& zstream = *(static_cast<z_stream*>(this->zdata));
40 42 zstream.zalloc = nullptr;
... ... @@ -70,9 +72,7 @@ Pl_Flate::Pl_Flate(
70 72 Pipeline(identifier, next),
71 73 m(std::make_unique<Members>(QIntC::to_size(out_bufsize_int), action))
72 74 {
73   - if (!next) {
74   - throw std::logic_error("Attempt to create Pl_Flate with nullptr as next");
75   - }
  75 + util::assertion(next, "Attempt to create Pl_Flate with nullptr as next");
76 76 }
77 77  
78 78 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
... ... @@ -107,10 +107,8 @@ Pl_Flate::warn(char const* msg, int code)
107 107 void
108 108 Pl_Flate::write(unsigned char const* data, size_t len)
109 109 {
110   - if (!m->outbuf) {
111   - throw std::logic_error(
112   - this->identifier + ": Pl_Flate: write() called after finish() called");
113   - }
  110 + util::assertion(
  111 + m->outbuf.get(), identifier + ": Pl_Flate: write() called after finish() called");
114 112 if (m->zopfli_buf) {
115 113 m->zopfli_buf->append(reinterpret_cast<char const*>(data), len);
116 114 return;
... ... @@ -131,9 +129,8 @@ Pl_Flate::write(unsigned char const* data, size_t len)
131 129 void
132 130 Pl_Flate::handleData(unsigned char const* data, size_t len, int flush)
133 131 {
134   - if (len > UINT_MAX) {
135   - throw std::runtime_error("Pl_Flate: zlib doesn't support data blocks larger than int");
136   - }
  132 + util::no_ci_rt_error_if(
  133 + len > UINT_MAX, "Pl_Flate: zlib doesn't support data blocks larger than int");
137 134 z_stream& zstream = *(static_cast<z_stream*>(m->zdata));
138 135 // zlib is known not to modify the data pointed to by next_in but doesn't declare the field
139 136 // value const unless compiled to do so.
... ... @@ -216,7 +213,6 @@ Pl_Flate::handleData(unsigned char const* data, size_t len, int flush)
216 213  
217 214 default:
218 215 checkError("data", err);
219   - break;
220 216 }
221 217 }
222 218 }
... ...
libqpdf/Pl_LZWDecoder.cc
... ... @@ -2,17 +2,17 @@
2 2  
3 3 #include <qpdf/QIntC.hh>
4 4 #include <qpdf/QTC.hh>
5   -#include <qpdf/QUtil.hh>
  5 +#include <qpdf/Util.hh>
6 6 #include <cstring>
7 7 #include <stdexcept>
8 8  
  9 +using namespace qpdf;
  10 +
9 11 Pl_LZWDecoder::Pl_LZWDecoder(char const* identifier, Pipeline* next, bool early_code_change) :
10 12 Pipeline(identifier, next),
11 13 code_change_delta(early_code_change)
12 14 {
13   - if (!next) {
14   - throw std::logic_error("Attempt to create Pl_LZWDecoder with nullptr as next");
15   - }
  15 + util::assertion(next, "Attempt to create Pl_LZWDecoder with nullptr as next");
16 16 }
17 17  
18 18 void
... ... @@ -78,21 +78,17 @@ Pl_LZWDecoder::sendNextCode()
78 78 unsigned char
79 79 Pl_LZWDecoder::getFirstChar(unsigned int code)
80 80 {
81   - unsigned char result = '\0';
82 81 if (code < 256) {
83   - result = static_cast<unsigned char>(code);
84   - } else if (code > 257) {
85   - unsigned int idx = code - 258;
86   - if (idx >= table.size()) {
87   - throw std::runtime_error("Pl_LZWDecoder::getFirstChar: table overflow");
88   - }
89   - Buffer& b = table.at(idx);
90   - result = b.getBuffer()[0];
91   - } else {
92   - throw std::runtime_error(
93   - "Pl_LZWDecoder::getFirstChar called with invalid code (" + std::to_string(code) + ")");
  82 + return static_cast<unsigned char>(code);
94 83 }
95   - return result;
  84 + util::no_ci_rt_error_if(
  85 + code <= 257,
  86 + "Pl_LZWDecoder::getFirstChar called with invalid code (" + std::to_string(code) + ")");
  87 +
  88 + unsigned int idx = code - 258;
  89 + util::no_ci_rt_error_if(idx >= table.size(), "Pl_LZWDecoder::getFirstChar: table overflow");
  90 + Buffer& b = table.at(idx);
  91 + return b.getBuffer()[0];
96 92 }
97 93  
98 94 void
... ... @@ -106,18 +102,16 @@ Pl_LZWDecoder::addToTable(unsigned char c)
106 102 tmp[0] = static_cast<unsigned char>(last_code);
107 103 last_data = tmp;
108 104 last_size = 1;
109   - } else if (last_code > 257) {
  105 + } else {
  106 + util::no_ci_rt_error_if(
  107 + last_code <= 257,
  108 + "Pl_LZWDecoder::addToTable called with invalid code (" + std::to_string(last_code) +
  109 + ")");
110 110 unsigned int idx = last_code - 258;
111   - if (idx >= table.size()) {
112   - throw std::runtime_error("Pl_LZWDecoder::addToTable: table overflow");
113   - }
  111 + util::no_ci_rt_error_if(idx >= table.size(), "Pl_LZWDecoder::addToTable: table overflow");
114 112 Buffer& b = table.at(idx);
115 113 last_data = b.getBuffer();
116 114 last_size = QIntC::to_uint(b.getSize());
117   - } else {
118   - throw std::runtime_error(
119   - "Pl_LZWDecoder::addToTable called with invalid code (" + std::to_string(last_code) +
120   - ")");
121 115 }
122 116  
123 117 Buffer entry(1 + last_size);
... ... @@ -158,19 +152,16 @@ Pl_LZWDecoder::handleCode(unsigned int code)
158 152 } else if (idx == table_size) {
159 153 // The encoder would have just created this entry, so the first character of
160 154 // this entry would have been the same as the first character of the last entry.
161   - QTC::TC("libtests", "Pl_LZWDecoder last was table size");
162 155 next_c = getFirstChar(last_code);
163 156 } else {
164 157 next_c = getFirstChar(code);
165 158 }
166 159 }
167 160 unsigned int new_idx = 258 + table_size;
168   - if (new_idx == 4096) {
169   - throw std::runtime_error("LZWDecoder: table full");
170   - }
  161 + util::no_ci_rt_error_if(new_idx == 4096, "LZWDecoder: table full");
171 162 addToTable(next_c);
172 163 unsigned int change_idx = new_idx + code_change_delta;
173   - if ((change_idx == 511) || (change_idx == 1023) || (change_idx == 2047)) {
  164 + if (change_idx == 511 || change_idx == 1023 || change_idx == 2047) {
174 165 ++code_size;
175 166 }
176 167 }
... ...
libqpdf/Pl_RC4.cc
1 1 #include <qpdf/Pl_RC4.hh>
2 2  
3 3 #include <qpdf/QUtil.hh>
  4 +#include <qpdf/Util.hh>
  5 +
  6 +using namespace qpdf;
4 7  
5 8 Pl_RC4::Pl_RC4(char const* identifier, Pipeline* next, std::string key, size_t out_bufsize) :
6 9 Pipeline(identifier, next),
7 10 out_bufsize(out_bufsize),
8 11 rc4(reinterpret_cast<unsigned char const*>(key.data()), static_cast<int>(key.size()))
9 12 {
10   - if (!next) {
11   - throw std::logic_error("Attempt to create Pl_RC4 with nullptr as next");
12   - }
  13 + util::assertion(next, "Attempt to create Pl_RC4 with nullptr as next");
13 14 this->outbuf = QUtil::make_shared_array<unsigned char>(out_bufsize);
14 15 }
15 16  
16 17 void
17 18 Pl_RC4::write(unsigned char const* data, size_t len)
18 19 {
19   - if (this->outbuf == nullptr) {
20   - throw std::logic_error(this->identifier + ": Pl_RC4: write() called after finish() called");
21   - }
  20 + util::assertion(outbuf.get(), "Pl_RC4: write() called after finish() called");
22 21  
23 22 size_t bytes_left = len;
24 23 unsigned char const* p = data;
... ... @@ -26,7 +25,6 @@ Pl_RC4::write(unsigned char const* data, size_t len)
26 25 while (bytes_left > 0) {
27 26 size_t bytes = (bytes_left < this->out_bufsize ? bytes_left : out_bufsize);
28 27 bytes_left -= bytes;
29   - // lgtm[cpp/weak-cryptographic-algorithm]
30 28 rc4.process(p, bytes, outbuf.get());
31 29 p += bytes;
32 30 next()->write(outbuf.get(), bytes);
... ...
libqpdf/Pl_SHA2.cc
... ... @@ -2,8 +2,9 @@
2 2  
3 3 #include <qpdf/QPDFCryptoProvider.hh>
4 4 #include <qpdf/QUtil.hh>
  5 +#include <qpdf/Util.hh>
5 6  
6   -#include <stdexcept>
  7 +using namespace qpdf;
7 8  
8 9 Pl_SHA2::Pl_SHA2(int bits, Pipeline* next) :
9 10 Pipeline("sha2", next)
... ... @@ -49,9 +50,7 @@ Pl_SHA2::finish()
49 50 void
50 51 Pl_SHA2::resetBits(int bits)
51 52 {
52   - if (in_progress) {
53   - throw std::logic_error("bit reset requested for in-progress SHA2 Pipeline");
54   - }
  53 + util::assertion(!in_progress, "bit reset requested for in-progress SHA2 Pipeline");
55 54 crypto = QPDFCryptoProvider::getImpl();
56 55 crypto->SHA2_init(bits);
57 56 }
... ... @@ -59,17 +58,13 @@ Pl_SHA2::resetBits(int bits)
59 58 std::string
60 59 Pl_SHA2::getRawDigest()
61 60 {
62   - if (in_progress) {
63   - throw std::logic_error("digest requested for in-progress SHA2 Pipeline");
64   - }
  61 + util::assertion(!in_progress, "digest requested for in-progress SHA2 Pipeline");
65 62 return crypto->SHA2_digest();
66 63 }
67 64  
68 65 std::string
69 66 Pl_SHA2::getHexDigest()
70 67 {
71   - if (in_progress) {
72   - throw std::logic_error("digest requested for in-progress SHA2 Pipeline");
73   - }
  68 + util::assertion(!in_progress, "digest requested for in-progress SHA2 Pipeline");
74 69 return QUtil::hex_encode(getRawDigest());
75 70 }
... ...
libqpdf/QPDF.cc
... ... @@ -672,10 +672,7 @@ QPDF::getXRefTable()
672 672 std::map<QPDFObjGen, QPDFXRefEntry> const&
673 673 Objects::xref_table()
674 674 {
675   - if (!m->parsed) {
676   - throw std::logic_error("QPDF::getXRefTable called before parsing.");
677   - }
678   -
  675 + util::assertion(m->parsed, "QPDF::getXRefTable called before parsing");
679 676 return m->xref_table;
680 677 }
681 678  
... ...
libqpdf/QPDFArgParser.cc
... ... @@ -56,7 +56,6 @@ QPDFArgParser::selectOptionTable(std::string const&amp; name)
56 56 {
57 57 auto t = m->option_tables.find(name);
58 58 if (t == m->option_tables.end()) {
59   - QTC::TC("libtests", "QPDFArgParser select unregistered table");
60 59 throw std::logic_error("QPDFArgParser: selecting unregistered option table " + name);
61 60 }
62 61 m->option_table = &(t->second);
... ... @@ -67,7 +66,6 @@ void
67 66 QPDFArgParser::registerOptionTable(std::string const& name, bare_arg_handler_t end_handler)
68 67 {
69 68 if (m->option_tables.contains(name)) {
70   - QTC::TC("libtests", "QPDFArgParser register registered table");
71 69 throw std::logic_error(
72 70 "QPDFArgParser: registering already registered option table " + name);
73 71 }
... ... @@ -80,7 +78,6 @@ QPDFArgParser::OptionEntry&amp;
80 78 QPDFArgParser::registerArg(std::string const& arg)
81 79 {
82 80 if (m->option_table->contains(arg)) {
83   - QTC::TC("libtests", "QPDFArgParser duplicate handler");
84 81 throw std::logic_error(
85 82 "QPDFArgParser: adding a duplicate handler for option " + arg + " in " +
86 83 m->option_table_name + " option table");
... ... @@ -138,7 +135,6 @@ QPDFArgParser::addInvalidChoiceHandler(std::string const&amp; arg, param_arg_handler
138 135 {
139 136 auto i = m->option_table->find(arg);
140 137 if (i == m->option_table->end()) {
141   - QTC::TC("libtests", "QPDFArgParser invalid choice handler to unknown");
142 138 throw std::logic_error(
143 139 "QPDFArgParser: attempt to add invalid choice handler to unknown argument");
144 140 }
... ... @@ -448,11 +444,9 @@ QPDFArgParser::parseArgs()
448 444 // Special case for -- option, which is used to break out of subparsers.
449 445 oep = m->option_table->find("--");
450 446 end_option = true;
451   - if (oep == m->option_table->end()) {
452   - // This is registered automatically, so this can't happen.
453   - throw std::logic_error("QPDFArgParser: -- handler not registered");
454   - }
455   - } else if ((arg[0] == '-') && (strcmp(arg, "-") != 0)) {
  447 + util::internal_error_if(
  448 + oep == m->option_table->end(), "QPDFArgParser: -- handler not registered");
  449 + } else if (arg[0] == '-' && strcmp(arg, "-") != 0) {
456 450 ++arg;
457 451 if (arg[0] == '-') {
458 452 // Be lax about -arg vs --arg
... ... @@ -678,15 +672,12 @@ QPDFArgParser::addHelpTopic(
678 672 std::string const& topic, std::string const& short_text, std::string const& long_text)
679 673 {
680 674 if (topic == "all") {
681   - QTC::TC("libtests", "QPDFArgParser add reserved help topic");
682 675 throw std::logic_error("QPDFArgParser: can't register reserved help topic " + topic);
683 676 }
684 677 if (topic.empty() || topic.at(0) == '-') {
685   - QTC::TC("libtests", "QPDFArgParser bad topic for help");
686 678 throw std::logic_error("QPDFArgParser: help topics must not start with -");
687 679 }
688 680 if (m->help_topics.contains(topic)) {
689   - QTC::TC("libtests", "QPDFArgParser add existing topic");
690 681 throw std::logic_error("QPDFArgParser: topic " + topic + " has already been added");
691 682 }
692 683  
... ... @@ -701,17 +692,14 @@ QPDFArgParser::addOptionHelp(
701 692 std::string const& short_text,
702 693 std::string const& long_text)
703 694 {
704   - if (!((option_name.length() > 2) && (option_name.at(0) == '-') && (option_name.at(1) == '-'))) {
705   - QTC::TC("libtests", "QPDFArgParser bad option for help");
  695 + if (!(option_name.length() > 2 && option_name.starts_with("--"))) {
706 696 throw std::logic_error("QPDFArgParser: options for help must start with --");
707 697 }
708 698 if (m->option_help.contains(option_name)) {
709   - QTC::TC("libtests", "QPDFArgParser duplicate option help");
710 699 throw std::logic_error("QPDFArgParser: option " + option_name + " already has help");
711 700 }
712 701 auto ht = m->help_topics.find(topic);
713 702 if (ht == m->help_topics.end()) {
714   - QTC::TC("libtests", "QPDFArgParser add to unknown topic");
715 703 throw std::logic_error(
716 704 "QPDFArgParser: unable to add option " + option_name + " to unknown help topic " +
717 705 topic);
... ...
libqpdf/qpdf/Util.hh
... ... @@ -35,6 +35,14 @@ namespace qpdf::util
35 35 }
36 36 }
37 37  
  38 + inline void
  39 + no_ci_rt_error_if(bool cond, std::string const& msg)
  40 + {
  41 + if (cond) {
  42 + throw std::runtime_error(msg);
  43 + }
  44 + }
  45 +
38 46 inline constexpr char
39 47 hex_decode_char(char digit)
40 48 {
... ...
libtests/libtests.testcov
1 1 ignored-scope: qpdf
2 2 Pl_LZWDecoder intermediate reset 0
3   -Pl_LZWDecoder last was table size 0
4 3 Pl_ASCII85Decoder ignore space 0
5   -Pl_ASCII85Decoder read z 0
6   -Pl_ASCII85Decoder no-op flush 0
7 4 Pl_ASCII85Decoder partial flush 1
8 5 bits leftover 1
9 6 bits bit_offset 2
... ... @@ -41,22 +38,12 @@ QPDFArgParser read args from stdin 0
41 38 QPDFArgParser read args from file 0
42 39 QPDFArgParser required choices 0
43 40 QPDFArgParser required parameter 0
44   -QPDFArgParser select unregistered table 0
45   -QPDFArgParser register registered table 0
46   -QPDFArgParser duplicate handler 0
47 41 QPDFArgParser missing -- 0
48 42 QPDFArgParser single dash 0
49 43 QPDFArgParser help option 0
50 44 QPDFArgParser positional 0
51 45 QPDFArgParser unrecognized 0
52 46 QPDFArgParser complete choices 0
53   -QPDFArgParser add reserved help topic 0
54   -QPDFArgParser add existing topic 0
55   -QPDFArgParser add to unknown topic 0
56   -QPDFArgParser duplicate option help 0
57   -QPDFArgParser bad option for help 0
58   -QPDFArgParser bad topic for help 0
59   -QPDFArgParser invalid choice handler to unknown 0
60 47 JSON parse junk after object 0
61 48 JSON parse invalid keyword 0
62 49 JSON parse expected colon 0
... ...
libtests/qtest/qutil/qutil.out
... ... @@ -137,3 +137,8 @@ D:20210209191925Z
137 137 done
138 138 ---- memory usage
139 139 memory usage okay
  140 +---- error handlers
  141 +caught exception: msg2
  142 +caught exception: INTERNAL ERROR: msg4
  143 +This is a qpdf bug. Please report at https://github.com/qpdf/qpdf/issues
  144 +caught exception: msg6
... ...
libtests/qutil.cc
... ... @@ -3,6 +3,8 @@
3 3 #include <qpdf/Pl_Buffer.hh>
4 4 #include <qpdf/QPDFSystemError.hh>
5 5 #include <qpdf/QUtil.hh>
  6 +#include <qpdf/Util.hh>
  7 +
6 8 #include <climits>
7 9 #include <cstdio>
8 10 #include <cstring>
... ... @@ -743,6 +745,29 @@ memory_usage_test()
743 745 std::cout << "memory usage okay" << '\n';
744 746 }
745 747  
  748 +void
  749 +error_handler_test()
  750 +{
  751 + qpdf::util::assertion(true, "msg1");
  752 + try {
  753 + qpdf::util::assertion(false, "msg2");
  754 + } catch (std::logic_error const& e) {
  755 + std::cout << "caught exception: " << e.what() << '\n';
  756 + }
  757 + qpdf::util::internal_error_if(false, "msg3");
  758 + try {
  759 + qpdf::util::internal_error_if(true, "msg4");
  760 + } catch (std::logic_error const& e) {
  761 + std::cout << "caught exception: " << e.what() << '\n';
  762 + }
  763 + qpdf::util::no_ci_rt_error_if(false, "msg5");
  764 + try {
  765 + qpdf::util::no_ci_rt_error_if(true, "msg6");
  766 + } catch (std::runtime_error const& e) {
  767 + std::cout << "caught exception: " << e.what() << '\n';
  768 + }
  769 +}
  770 +
746 771 int
747 772 main(int argc, char* argv[])
748 773 {
... ... @@ -782,6 +807,8 @@ main(int argc, char* argv[])
782 807 is_long_long_test();
783 808 std::cout << "---- memory usage" << '\n';
784 809 memory_usage_test();
  810 + std::cout << "---- error handlers" << '\n';
  811 + error_handler_test();
785 812 } catch (std::exception& e) {
786 813 std::cout << "unexpected exception: " << e.what() << '\n';
787 814 }
... ...