Coverage Report

Created: 2026-05-30 06:15

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
169
    {
28
169
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
169
    }
void qpdf::util::assertion<char const (&) [52]>(bool, char const (&) [52])
Line
Count
Source
27
169
    {
28
169
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
169
    }
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [49]>(bool, char const (&) [49])
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [53]>(bool, char const (&) [53])
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> >&)
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> >&&)
32
33
    template <typename T>
34
    inline void
35
    internal_error_if(bool cond, T&& msg)
36
    {
37
        if (cond) {
38
            throw std::logic_error("INTERNAL ERROR: "s.append(std::forward<T>(msg))
39
                                       .append(
40
                                           "\nThis is a qpdf bug. Please report at "
41
                                           "https://github.com/qpdf/qpdf/issues"));
42
        }
43
    }
44
45
    template <typename T>
46
    inline void
47
    no_ci_rt_error_if(bool cond, T&& msg)
48
169
    {
49
169
        if (cond) {
50
23
            throw std::runtime_error(std::forward<T>(msg));
51
23
        }
52
169
    }
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
1.01k
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
338
                          std::numeric_limits<T>::min())) {
67
338
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
338
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
845
                          std::numeric_limits<T>::max())) {
74
845
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
0
                return false;
76
0
            }
77
845
        }
78
845
        return true;
79
1.01k
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralEyQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
676
    {
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
676
                          std::numeric_limits<T>::max())) {
74
676
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
0
                return false;
76
0
            }
77
676
        }
78
676
        return true;
79
676
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralElQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
169
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
169
                          std::numeric_limits<T>::min())) {
67
169
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
169
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
169
                          std::numeric_limits<T>::max())) {
74
169
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
0
                return false;
76
0
            }
77
169
        }
78
169
        return true;
79
169
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralEiQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
169
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
169
                          std::numeric_limits<T>::min())) {
67
169
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
169
        }
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
169
        return true;
79
169
    }
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
0
    {
100
0
        return digit <= '9' && digit >= '0'
101
0
            ? char(digit - '0')
102
0
            : (digit >= 'a' ? char(digit - 'a' + 10)
103
0
                            : (digit >= 'A' ? char(digit - 'A' + 10) : '\20'));
104
0
    }
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
0
    {
115
0
        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\v';
116
0
    }
117
118
    inline bool
119
    is_digit(char ch)
120
0
    {
121
0
        return (ch >= '0' && ch <= '9');
122
0
    }
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
0
    {
128
0
        static auto constexpr hexchars = "0123456789abcdef";
129
0
        return {'#', hexchars[static_cast<unsigned char>(c) >> 4], hexchars[c & 0x0f]};
130
0
    }
131
132
    // Numerically increment a digit string. Ignore the last 'tail' characters.
133
    inline void
134
    increment(std::string& s, int tail = 0)
135
0
    {
136
0
        auto end = s.rend();
137
0
        for (auto it = s.rbegin() + tail; it != end; ++it) {
138
0
            ++*it;
139
0
            if (*it != ':') {
140
0
                return;
141
0
            }
142
0
            *it = '0';
143
0
        }
144
0
        s.insert(0, 1, '1');
145
0
    }
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