Coverage Report

Created: 2026-07-30 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qpdf/libqpdf/qpdf/Util.hh
Line
Count
Source
1
#ifndef UTIL_HH
2
#define UTIL_HH
3
4
#include <qpdf/assert_debug.h>
5
6
#include <concepts>
7
#include <cstdint>
8
#include <limits>
9
#include <stdexcept>
10
#include <string>
11
#include <utility>
12
13
using namespace std::literals;
14
15
namespace qpdf::util
16
{
17
    // qpdf::util is a collection of useful utility functions for qpdf internal use. It includes
18
    // inline functions, some of which are exposed as regular functions in QUtil. Implementations
19
    // are in QUtil.cc.
20
21
    // Throw a logic_error if 'cond' does not hold.
22
    //
23
    // DO NOT USE unless it is impractical or unnecessary to cover violations during CI Testing.
24
    template <typename T>
25
    inline void
26
    assertion(bool cond, T&& msg)
27
42.2M
    {
28
42.2M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
42.2M
    }
void qpdf::util::assertion<char const (&) [44]>(bool, char const (&) [44])
Line
Count
Source
27
131k
    {
28
131k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
131k
    }
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [71]>(bool, char const (&) [71])
void qpdf::util::assertion<char const (&) [57]>(bool, char const (&) [57])
Line
Count
Source
27
2.02k
    {
28
2.02k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
2.02k
    }
void qpdf::util::assertion<char const (&) [51]>(bool, char const (&) [51])
Line
Count
Source
27
1.07k
    {
28
1.07k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
1.07k
    }
void qpdf::util::assertion<char const (&) [41]>(bool, char const (&) [41])
Line
Count
Source
27
9.61k
    {
28
9.61k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
9.61k
    }
void qpdf::util::assertion<char const (&) [45]>(bool, char const (&) [45])
Line
Count
Source
27
1.26M
    {
28
1.26M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
1.26M
    }
void qpdf::util::assertion<char const (&) [121]>(bool, char const (&) [121])
Line
Count
Source
27
595k
    {
28
595k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
595k
    }
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [61]>(bool, char const (&) [61])
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [74]>(bool, char const (&) [74])
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [111]>(bool, char const (&) [111])
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [106]>(bool, char const (&) [106])
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [40]>(bool, char const (&) [40])
void qpdf::util::assertion<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
27
290k
    {
28
290k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
290k
    }
void qpdf::util::assertion<char const (&) [54]>(bool, char const (&) [54])
Line
Count
Source
27
479k
    {
28
479k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
479k
    }
void qpdf::util::assertion<char const (&) [53]>(bool, char const (&) [53])
Line
Count
Source
27
23.5k
    {
28
23.5k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
23.5k
    }
void qpdf::util::assertion<char const (&) [35]>(bool, char const (&) [35])
Line
Count
Source
27
4.86k
    {
28
4.86k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
4.86k
    }
void qpdf::util::assertion<char const (&) [68]>(bool, char const (&) [68])
Line
Count
Source
27
2.29k
    {
28
2.29k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
2.29k
    }
Unexecuted instantiation: void qpdf::util::assertion<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
void qpdf::util::assertion<char const (&) [83]>(bool, char const (&) [83])
Line
Count
Source
27
111k
    {
28
111k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
111k
    }
void qpdf::util::assertion<char const (&) [50]>(bool, char const (&) [50])
Line
Count
Source
27
294k
    {
28
294k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
294k
    }
void qpdf::util::assertion<char const (&) [43]>(bool, char const (&) [43])
Line
Count
Source
27
15.3k
    {
28
15.3k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
15.3k
    }
void qpdf::util::assertion<char const (&) [52]>(bool, char const (&) [52])
Line
Count
Source
27
38.8M
    {
28
38.8M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
38.8M
    }
void qpdf::util::assertion<char const (&) [58]>(bool, char const (&) [58])
Line
Count
Source
27
1.62k
    {
28
1.62k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
1.62k
    }
void qpdf::util::assertion<char const (&) [48]>(bool, char const (&) [48])
Line
Count
Source
27
16.4k
    {
28
16.4k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
16.4k
    }
void qpdf::util::assertion<char const (&) [46]>(bool, char const (&) [46])
Line
Count
Source
27
10.0k
    {
28
10.0k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
10.0k
    }
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [49]>(bool, char const (&) [49])
void qpdf::util::assertion<char const (&) [47]>(bool, char const (&) [47])
Line
Count
Source
27
139k
    {
28
139k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
139k
    }
void qpdf::util::assertion<char const (&) [56]>(bool, char const (&) [56])
Line
Count
Source
27
749
    {
28
749
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
749
    }
32
33
    template <typename T>
34
    inline void
35
    internal_error_if(bool cond, T&& msg)
36
5.36M
    {
37
5.36M
        if (cond) {
38
0
            throw std::logic_error("INTERNAL ERROR: "s.append(std::forward<T>(msg))
39
0
                                       .append(
40
0
                                           "\nThis is a qpdf bug. Please report at "
41
0
                                           "https://github.com/qpdf/qpdf/issues"));
42
0
        }
43
5.36M
    }
void qpdf::util::internal_error_if<char const (&) [29]>(bool, char const (&) [29])
Line
Count
Source
36
4.08M
    {
37
4.08M
        if (cond) {
38
0
            throw std::logic_error("INTERNAL ERROR: "s.append(std::forward<T>(msg))
39
0
                                       .append(
40
0
                                           "\nThis is a qpdf bug. Please report at "
41
0
                                           "https://github.com/qpdf/qpdf/issues"));
42
0
        }
43
4.08M
    }
void qpdf::util::internal_error_if<char const (&) [28]>(bool, char const (&) [28])
Line
Count
Source
36
901k
    {
37
901k
        if (cond) {
38
0
            throw std::logic_error("INTERNAL ERROR: "s.append(std::forward<T>(msg))
39
0
                                       .append(
40
0
                                           "\nThis is a qpdf bug. Please report at "
41
0
                                           "https://github.com/qpdf/qpdf/issues"));
42
0
        }
43
901k
    }
void qpdf::util::internal_error_if<char const (&) [34]>(bool, char const (&) [34])
Line
Count
Source
36
380k
    {
37
380k
        if (cond) {
38
0
            throw std::logic_error("INTERNAL ERROR: "s.append(std::forward<T>(msg))
39
0
                                       .append(
40
0
                                           "\nThis is a qpdf bug. Please report at "
41
0
                                           "https://github.com/qpdf/qpdf/issues"));
42
0
        }
43
380k
    }
Unexecuted instantiation: void qpdf::util::internal_error_if<char const (&) [44]>(bool, char const (&) [44])
44
45
    template <typename T>
46
    inline void
47
    no_ci_rt_error_if(bool cond, T&& msg)
48
4.27M
    {
49
4.27M
        if (cond) {
50
388
            throw std::runtime_error(std::forward<T>(msg));
51
388
        }
52
4.27M
    }
Unexecuted instantiation: void qpdf::util::no_ci_rt_error_if<char const (&) [58]>(bool, char const (&) [58])
void qpdf::util::no_ci_rt_error_if<char const (&) [51]>(bool, char const (&) [51])
Line
Count
Source
48
1.52M
    {
49
1.52M
        if (cond) {
50
18
            throw std::runtime_error(std::forward<T>(msg));
51
18
        }
52
1.52M
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [23]>(bool, char const (&) [23])
Line
Count
Source
48
2.45M
    {
49
2.45M
        if (cond) {
50
243
            throw std::runtime_error(std::forward<T>(msg));
51
243
        }
52
2.45M
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [44]>(bool, char const (&) [44])
Line
Count
Source
48
21.0k
    {
49
21.0k
        if (cond) {
50
37
            throw std::runtime_error(std::forward<T>(msg));
51
37
        }
52
21.0k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [73]>(bool, char const (&) [73])
Line
Count
Source
48
6.16k
    {
49
6.16k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
6.16k
    }
void qpdf::util::no_ci_rt_error_if<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
48
41.1k
    {
49
41.1k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
41.1k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [69]>(bool, char const (&) [69])
Line
Count
Source
48
18.0k
    {
49
18.0k
        if (cond) {
50
22
            throw std::runtime_error(std::forward<T>(msg));
51
22
        }
52
18.0k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [59]>(bool, char const (&) [59])
Line
Count
Source
48
167k
    {
49
167k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
167k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [42]>(bool, char const (&) [42])
Line
Count
Source
48
20.6k
    {
49
20.6k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
20.6k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [49]>(bool, char const (&) [49])
Line
Count
Source
48
2.36k
    {
49
2.36k
        if (cond) {
50
11
            throw std::runtime_error(std::forward<T>(msg));
51
11
        }
52
2.36k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [88]>(bool, char const (&) [88])
Line
Count
Source
48
1.60k
    {
49
1.60k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
1.60k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [45]>(bool, char const (&) [45])
Line
Count
Source
48
1.60k
    {
49
1.60k
        if (cond) {
50
3
            throw std::runtime_error(std::forward<T>(msg));
51
3
        }
52
1.60k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [32]>(bool, char const (&) [32])
Line
Count
Source
48
1.60k
    {
49
1.60k
        if (cond) {
50
6
            throw std::runtime_error(std::forward<T>(msg));
51
6
        }
52
1.60k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [35]>(bool, char const (&) [35])
Line
Count
Source
48
13.6k
    {
49
13.6k
        if (cond) {
50
12
            throw std::runtime_error(std::forward<T>(msg));
51
12
        }
52
13.6k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [53]>(bool, char const (&) [53])
Line
Count
Source
48
749
    {
49
749
        if (cond) {
50
12
            throw std::runtime_error(std::forward<T>(msg));
51
12
        }
52
749
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [92]>(bool, char const (&) [92])
Line
Count
Source
48
733
    {
49
733
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
733
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [36]>(bool, char const (&) [36])
Line
Count
Source
48
730
    {
49
730
        if (cond) {
50
24
            throw std::runtime_error(std::forward<T>(msg));
51
24
        }
52
730
    }
53
54
    /// @brief Return true if `val` fits in `T` (predicate only).
55
    ///
56
    /// @tparam T integral target type
57
    /// @param val value to test
58
    /// @return true if `val` fits in `T`
59
    template <typename T>
60
        requires std::integral<T>
61
    bool
62
    fits(std::integral auto val)
63
151k
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
48.8k
                          std::numeric_limits<T>::min())) {
67
48.8k
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
48.8k
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
126k
                          std::numeric_limits<T>::max())) {
74
126k
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
6
                return false;
76
6
            }
77
126k
        }
78
126k
        return true;
79
151k
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralElQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
24.4k
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
24.4k
                          std::numeric_limits<T>::min())) {
67
24.4k
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
24.4k
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
24.4k
                          std::numeric_limits<T>::max())) {
74
24.4k
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
0
                return false;
76
0
            }
77
24.4k
        }
78
24.4k
        return true;
79
24.4k
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralEiQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
24.4k
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
24.4k
                          std::numeric_limits<T>::min())) {
67
24.4k
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
24.4k
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
                          std::numeric_limits<T>::max())) {
74
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
                return false;
76
            }
77
        }
78
24.4k
        return true;
79
24.4k
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralEyQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
102k
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
                          std::numeric_limits<T>::min())) {
67
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
                return false;
69
            }
70
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
102k
                          std::numeric_limits<T>::max())) {
74
102k
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
6
                return false;
76
6
            }
77
102k
        }
78
102k
        return true;
79
102k
    }
Unexecuted instantiation: _ZN4qpdf4util4fitsIlTkNSt3__18integralEjQsr3stdE8integralIT_EEEbT0_
Unexecuted instantiation: _ZN4qpdf4util4fitsIiTkNSt3__18integralEjQsr3stdE8integralIT_EEEbT0_
80
81
    /// @brief Convert `val` to `T`; throws std::range_error if out-of-range.
82
    ///
83
    /// @tparam T integral target type
84
    /// @param val value to convert
85
    /// @return Converted value as `T`
86
    template <typename T>
87
        requires std::integral<T>
88
    T
89
    to(std::integral auto val)
90
    {
91
        if (!fits<T>(val)) {
92
            throw std::range_error("out of range converting integral values");
93
        }
94
        return static_cast<T>(val);
95
    }
96
97
    inline constexpr char
98
    hex_decode_char(char digit)
99
839k
    {
100
839k
        return digit <= '9' && digit >= '0'
101
839k
            ? char(digit - '0')
102
839k
            : (digit >= 'a' ? char(digit - 'a' + 10)
103
563k
                            : (digit >= 'A' ? char(digit - 'A' + 10) : '\20'));
104
839k
    }
105
106
    inline constexpr bool
107
    is_hex_digit(char ch)
108
0
    {
109
0
        return hex_decode_char(ch) < '\20';
110
0
    }
111
112
    inline constexpr bool
113
    is_space(char ch)
114
47.6M
    {
115
47.6M
        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\v';
116
47.6M
    }
117
118
    inline bool
119
    is_digit(char ch)
120
20.9M
    {
121
20.9M
        return (ch >= '0' && ch <= '9');
122
20.9M
    }
123
124
    // Returns lower-case hex-encoded version of the char including a leading "#".
125
    inline std::string
126
    hex_encode_char(char c)
127
2.27M
    {
128
2.27M
        static auto constexpr hexchars = "0123456789abcdef";
129
2.27M
        return {'#', hexchars[static_cast<unsigned char>(c) >> 4], hexchars[c & 0x0f]};
130
2.27M
    }
131
132
    // Numerically increment a digit string. Ignore the last 'tail' characters.
133
    inline void
134
    increment(std::string& s, int tail = 0)
135
43.6k
    {
136
43.6k
        auto end = s.rend();
137
48.2k
        for (auto it = s.rbegin() + tail; it != end; ++it) {
138
47.8k
            ++*it;
139
47.8k
            if (*it != ':') {
140
43.2k
                return;
141
43.2k
            }
142
4.60k
            *it = '0';
143
4.60k
        }
144
446
        s.insert(0, 1, '1');
145
446
    }
146
147
    inline bool
148
    is_utf16(std::string const& str)
149
0
    {
150
0
        return str.starts_with("\xfe\xff") || str.starts_with("\xff\xfe");
151
0
    }
152
153
    inline bool
154
    is_explicit_utf8(std::string const& str)
155
0
    {
156
        // QPDF_String.cc knows that this is a 3-byte sequence.
157
0
        return str.starts_with("\xef\xbb\xbf");
158
0
    }
159
160
    std::string random_string(size_t len);
161
162
    // Test helpers
163
164
    /// @brief Predicate: returns true if invoking `f()` throws an exception of type `E`.
165
    ///
166
    /// Internal test helper used by unit tests: call `throws<SomeException>([](){ ... })` and
167
    /// it returns true when the callable throws `SomeException` and false otherwise.
168
    /// The callable must be invocable with no arguments.
169
    template <typename E, typename F>
170
        requires std::invocable<F>
171
    inline bool
172
    throws(F&& f)
173
    {
174
        try {
175
            std::forward<F>(f)();
176
            return false;
177
        } catch (E const&) {
178
            return true;
179
        } catch (...) {
180
            return false;
181
        }
182
    }
183
184
} // namespace qpdf::util
185
186
#endif // UTIL_HH