/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 |