Coverage Report

Created: 2026-07-30 07:17

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
2.09G
    {
28
2.09G
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
2.09G
    }
void qpdf::util::assertion<char const (&) [56]>(bool, char const (&) [56])
Line
Count
Source
27
10.8k
    {
28
10.8k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
10.8k
    }
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
42.6M
    {
28
42.6M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
42.6M
    }
void qpdf::util::assertion<char const (&) [44]>(bool, char const (&) [44])
Line
Count
Source
27
1.46M
    {
28
1.46M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
1.46M
    }
void qpdf::util::assertion<char const (&) [54]>(bool, char const (&) [54])
Line
Count
Source
27
3.54M
    {
28
3.54M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
3.54M
    }
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
35.2k
    {
28
35.2k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
35.2k
    }
void qpdf::util::assertion<char const (&) [51]>(bool, char const (&) [51])
Line
Count
Source
27
15.8k
    {
28
15.8k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
15.8k
    }
void qpdf::util::assertion<char const (&) [41]>(bool, char const (&) [41])
Line
Count
Source
27
38.1k
    {
28
38.1k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
38.1k
    }
void qpdf::util::assertion<char const (&) [40]>(bool, char const (&) [40])
Line
Count
Source
27
52.0k
    {
28
52.0k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
52.0k
    }
void qpdf::util::assertion<char const (&) [46]>(bool, char const (&) [46])
Line
Count
Source
27
220k
    {
28
220k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
220k
    }
void qpdf::util::assertion<char const (&) [25]>(bool, char const (&) [25])
Line
Count
Source
27
275k
    {
28
275k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
275k
    }
void qpdf::util::assertion<char const (&) [42]>(bool, char const (&) [42])
Line
Count
Source
27
30.9k
    {
28
30.9k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
30.9k
    }
void qpdf::util::assertion<char const (&) [34]>(bool, char const (&) [34])
Line
Count
Source
27
11.9k
    {
28
11.9k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
11.9k
    }
void qpdf::util::assertion<char const (&) [72]>(bool, char const (&) [72])
Line
Count
Source
27
244k
    {
28
244k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
244k
    }
void qpdf::util::assertion<char const (&) [45]>(bool, char const (&) [45])
Line
Count
Source
27
19.7M
    {
28
19.7M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
19.7M
    }
void qpdf::util::assertion<char const (&) [53]>(bool, char const (&) [53])
Line
Count
Source
27
459k
    {
28
459k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
459k
    }
void qpdf::util::assertion<char const (&) [35]>(bool, char const (&) [35])
Line
Count
Source
27
82.5k
    {
28
82.5k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
82.5k
    }
void qpdf::util::assertion<char const (&) [68]>(bool, char const (&) [68])
Line
Count
Source
27
72.9k
    {
28
72.9k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
72.9k
    }
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
1.09M
    {
28
1.09M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
1.09M
    }
void qpdf::util::assertion<char const (&) [50]>(bool, char const (&) [50])
Line
Count
Source
27
14.2M
    {
28
14.2M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
14.2M
    }
void qpdf::util::assertion<char const (&) [43]>(bool, char const (&) [43])
Line
Count
Source
27
206k
    {
28
206k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
206k
    }
void qpdf::util::assertion<char const (&) [52]>(bool, char const (&) [52])
Line
Count
Source
27
2.00G
    {
28
2.00G
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
2.00G
    }
void qpdf::util::assertion<char const (&) [58]>(bool, char const (&) [58])
Line
Count
Source
27
23.1k
    {
28
23.1k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
23.1k
    }
void qpdf::util::assertion<char const (&) [48]>(bool, char const (&) [48])
Line
Count
Source
27
509k
    {
28
509k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
509k
    }
void qpdf::util::assertion<char const (&) [49]>(bool, char const (&) [49])
Line
Count
Source
27
33.1k
    {
28
33.1k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
33.1k
    }
void qpdf::util::assertion<char const (&) [47]>(bool, char const (&) [47])
Line
Count
Source
27
6.93M
    {
28
6.93M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
6.93M
    }
void qpdf::util::assertion<char const (&) [65]>(bool, char const (&) [65])
Line
Count
Source
27
11.0k
    {
28
11.0k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
11.0k
    }
void qpdf::util::assertion<char const (&) [121]>(bool, char const (&) [121])
Line
Count
Source
27
1.96M
    {
28
1.96M
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
1.96M
    }
Unexecuted instantiation: void qpdf::util::assertion<char const (&) [61]>(bool, char const (&) [61])
void qpdf::util::assertion<char const (&) [74]>(bool, char const (&) [74])
Line
Count
Source
27
38.0k
    {
28
38.0k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
38.0k
    }
void qpdf::util::assertion<char const (&) [111]>(bool, char const (&) [111])
Line
Count
Source
27
34.2k
    {
28
34.2k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
34.2k
    }
void qpdf::util::assertion<char const (&) [106]>(bool, char const (&) [106])
Line
Count
Source
27
34.0k
    {
28
34.0k
        if (!cond) {
29
0
            throw std::logic_error(std::forward<T>(msg));
30
0
        }
31
34.0k
    }
32
33
    template <typename T>
34
    inline void
35
    internal_error_if(bool cond, T&& msg)
36
112M
    {
37
112M
        if (cond) {
38
16
            throw std::logic_error("INTERNAL ERROR: "s.append(std::forward<T>(msg))
39
16
                                       .append(
40
16
                                           "\nThis is a qpdf bug. Please report at "
41
16
                                           "https://github.com/qpdf/qpdf/issues"));
42
16
        }
43
112M
    }
Unexecuted instantiation: void qpdf::util::internal_error_if<char const (&) [44]>(bool, char const (&) [44])
void qpdf::util::internal_error_if<char const (&) [29]>(bool, char const (&) [29])
Line
Count
Source
36
33.2M
    {
37
33.2M
        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
33.2M
    }
void qpdf::util::internal_error_if<char const (&) [28]>(bool, char const (&) [28])
Line
Count
Source
36
38.3M
    {
37
38.3M
        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
38.3M
    }
void qpdf::util::internal_error_if<char const (&) [34]>(bool, char const (&) [34])
Line
Count
Source
36
40.6M
    {
37
40.6M
        if (cond) {
38
16
            throw std::logic_error("INTERNAL ERROR: "s.append(std::forward<T>(msg))
39
16
                                       .append(
40
16
                                           "\nThis is a qpdf bug. Please report at "
41
16
                                           "https://github.com/qpdf/qpdf/issues"));
42
16
        }
43
40.6M
    }
44
45
    template <typename T>
46
    inline void
47
    no_ci_rt_error_if(bool cond, T&& msg)
48
93.1M
    {
49
93.1M
        if (cond) {
50
4.71k
            throw std::runtime_error(std::forward<T>(msg));
51
4.71k
        }
52
93.1M
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [53]>(bool, char const (&) [53])
Line
Count
Source
48
10.8k
    {
49
10.8k
        if (cond) {
50
79
            throw std::runtime_error(std::forward<T>(msg));
51
79
        }
52
10.8k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [51]>(bool, char const (&) [51])
Line
Count
Source
48
24.6M
    {
49
24.6M
        if (cond) {
50
307
            throw std::runtime_error(std::forward<T>(msg));
51
307
        }
52
24.6M
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [92]>(bool, char const (&) [92])
Line
Count
Source
48
10.6k
    {
49
10.6k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
10.6k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [49]>(bool, char const (&) [49])
Line
Count
Source
48
95.1k
    {
49
95.1k
        if (cond) {
50
104
            throw std::runtime_error(std::forward<T>(msg));
51
104
        }
52
95.1k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [36]>(bool, char const (&) [36])
Line
Count
Source
48
10.6k
    {
49
10.6k
        if (cond) {
50
147
            throw std::runtime_error(std::forward<T>(msg));
51
147
        }
52
10.6k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [23]>(bool, char const (&) [23])
Line
Count
Source
48
29.4M
    {
49
29.4M
        if (cond) {
50
1.03k
            throw std::runtime_error(std::forward<T>(msg));
51
1.03k
        }
52
29.4M
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [44]>(bool, char const (&) [44])
Line
Count
Source
48
259k
    {
49
259k
        if (cond) {
50
487
            throw std::runtime_error(std::forward<T>(msg));
51
487
        }
52
259k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [73]>(bool, char const (&) [73])
Line
Count
Source
48
55.8k
    {
49
55.8k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
55.8k
    }
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
513k
    {
49
513k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
513k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [69]>(bool, char const (&) [69])
Line
Count
Source
48
585k
    {
49
585k
        if (cond) {
50
295
            throw std::runtime_error(std::forward<T>(msg));
51
295
        }
52
585k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [59]>(bool, char const (&) [59])
Line
Count
Source
48
35.9M
    {
49
35.9M
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
35.9M
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [42]>(bool, char const (&) [42])
Line
Count
Source
48
264k
    {
49
264k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
264k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [88]>(bool, char const (&) [88])
Line
Count
Source
48
84.0k
    {
49
84.0k
        if (cond) {
50
0
            throw std::runtime_error(std::forward<T>(msg));
51
0
        }
52
84.0k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [45]>(bool, char const (&) [45])
Line
Count
Source
48
84.0k
    {
49
84.0k
        if (cond) {
50
14
            throw std::runtime_error(std::forward<T>(msg));
51
14
        }
52
84.0k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [32]>(bool, char const (&) [32])
Line
Count
Source
48
84.0k
    {
49
84.0k
        if (cond) {
50
120
            throw std::runtime_error(std::forward<T>(msg));
51
120
        }
52
84.0k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [35]>(bool, char const (&) [35])
Line
Count
Source
48
867k
    {
49
867k
        if (cond) {
50
328
            throw std::runtime_error(std::forward<T>(msg));
51
328
        }
52
867k
    }
void qpdf::util::no_ci_rt_error_if<char const (&) [58]>(bool, char const (&) [58])
Line
Count
Source
48
67.2k
    {
49
67.2k
        if (cond) {
50
1.79k
            throw std::runtime_error(std::forward<T>(msg));
51
1.79k
        }
52
67.2k
    }
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
2.06M
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
623k
                          std::numeric_limits<T>::min())) {
67
623k
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
623k
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
1.74M
                          std::numeric_limits<T>::max())) {
74
1.74M
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
25
                return false;
76
25
            }
77
1.74M
        }
78
1.74M
        return true;
79
2.06M
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralEyQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
1.43M
    {
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
1.43M
                          std::numeric_limits<T>::max())) {
74
1.43M
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
25
                return false;
76
25
            }
77
1.43M
        }
78
1.43M
        return true;
79
1.43M
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralElQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
311k
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
311k
                          std::numeric_limits<T>::min())) {
67
311k
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
311k
        }
71
        if constexpr (std::cmp_greater(
72
                          std::numeric_limits<decltype(val)>::max(),
73
311k
                          std::numeric_limits<T>::max())) {
74
311k
            if (std::cmp_greater(val, std::numeric_limits<T>::max())) {
75
0
                return false;
76
0
            }
77
311k
        }
78
311k
        return true;
79
311k
    }
_ZN4qpdf4util4fitsIjTkNSt3__18integralEiQsr3stdE8integralIT_EEEbT0_
Line
Count
Source
63
311k
    {
64
        if constexpr (std::cmp_less(
65
                          std::numeric_limits<decltype(val)>::min(),
66
311k
                          std::numeric_limits<T>::min())) {
67
311k
            if (std::cmp_less(val, std::numeric_limits<T>::min())) {
68
0
                return false;
69
0
            }
70
311k
        }
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
311k
        return true;
79
311k
    }
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
170M
    {
100
170M
        return digit <= '9' && digit >= '0'
101
170M
            ? char(digit - '0')
102
170M
            : (digit >= 'a' ? char(digit - 'a' + 10)
103
27.4M
                            : (digit >= 'A' ? char(digit - 'A' + 10) : '\20'));
104
170M
    }
105
106
    inline constexpr bool
107
    is_hex_digit(char ch)
108
54.4k
    {
109
54.4k
        return hex_decode_char(ch) < '\20';
110
54.4k
    }
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
196M
    {
121
196M
        return (ch >= '0' && ch <= '9');
122
196M
    }
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
292M
    {
128
292M
        static auto constexpr hexchars = "0123456789abcdef";
129
292M
        return {'#', hexchars[static_cast<unsigned char>(c) >> 4], hexchars[c & 0x0f]};
130
292M
    }
131
132
    // Numerically increment a digit string. Ignore the last 'tail' characters.
133
    inline void
134
    increment(std::string& s, int tail = 0)
135
1.00M
    {
136
1.00M
        auto end = s.rend();
137
1.11M
        for (auto it = s.rbegin() + tail; it != end; ++it) {
138
1.10M
            ++*it;
139
1.10M
            if (*it != ':') {
140
993k
                return;
141
993k
            }
142
109k
            *it = '0';
143
109k
        }
144
13.4k
        s.insert(0, 1, '1');
145
13.4k
    }
146
147
    inline bool
148
    is_utf16(std::string const& str)
149
2.08M
    {
150
2.08M
        return str.starts_with("\xfe\xff") || str.starts_with("\xff\xfe");
151
2.08M
    }
152
153
    inline bool
154
    is_explicit_utf8(std::string const& str)
155
1.82M
    {
156
        // QPDF_String.cc knows that this is a 3-byte sequence.
157
1.82M
        return str.starts_with("\xef\xbb\xbf");
158
1.82M
    }
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