forked from Exiv2/exiv2
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsafe_op.hpp
330 lines (304 loc) · 13.1 KB
/
safe_op.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004-2021 Exiv2 authors
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
*/
#ifndef SAFE_OP_HPP_
#define SAFE_OP_HPP_
#include <limits>
#include <stdexcept>
#ifdef _MSC_VER
#include <Intsafe.h>
#endif
/*!
* @brief Arithmetic operations with overflow checks
*/
namespace Safe
{
/*!
* @brief Helper functions for providing integer overflow checks.
*
* This namespace contains internal helper functions fallback_$op_overflow
* and builtin_$op_overflow (where $op is an arithmetic operation like add,
* subtract, etc.). Both provide the following interface:
*
* bool fallback/builtin_$op_overflow(T first, T second, T& result);
*
* where T is an integer type.
*
* Each function performs checks whether first $op second can be safely
* performed without overflows. If yes, the result is saved in result and
* false is returned. Otherwise true is returned and the contents of result
* are unspecified.
*
* fallback_$op_overflow implements a portable but slower overflow check.
* builtin_$op_overflow uses compiler builtins (when available) and should
* be faster. As builtins are not available for all types,
* builtin_$op_overflow falls back to fallback_$op_overflow when no builtin
* is available.
*/
namespace Internal
{
/*!
* @brief Helper struct to determine whether a type is signed or unsigned
*
* This struct is a backport of std::is_signed from C++11. It has a public
* enum with the property VALUE which is true when the type is signed or
* false if it is unsigned.
*/
template <typename T>
struct is_signed
{
enum
{
VALUE = T(-1) < T(0)
};
};
/*!
* @brief Helper struct for SFINAE, from C++11
* This struct has a public typedef called type typedef'd to T if B is
* true. Otherwise there is no typedef.
*/
template <bool B, class T = void>
struct enable_if
{
};
/*!
* @brief Specialization of enable_if for the case B == true
*/
template <class T>
struct enable_if<true, T>
{
using type = T;
};
/*!
* @brief Check the addition of two numbers for overflows for signed
* integer types larger than int or with the same size as int.
*
* This function performs a check if summand_1 + summand_2 would
* overflow and returns true in that case. If no overflow occurs,
* the sum is saved in result and false is returned.
*
* @return true on overflow, false on no overflow
*
* @param[in] summand_1, summand_2 The summands with are added
* @param[out] result Result of the addition, only populated when no
* overflow occurs.
*
* Further information:
* https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
*/
template <typename T>
typename enable_if<is_signed<T>::VALUE && sizeof(T) >= sizeof(int), bool>::type fallback_add_overflow(
T summand_1, T summand_2, T& result)
{
if (((summand_2 >= 0) && (summand_1 > std::numeric_limits<T>::max() - summand_2)) ||
((summand_2 < 0) && (summand_1 < std::numeric_limits<T>::min() - summand_2))) {
return true;
} else {
result = summand_1 + summand_2;
return false;
}
}
/*!
* @brief Check the addition of two numbers for overflows for signed
* integer types smaller than int.
*
* This function adds summand_1 and summand_2 exploiting integer
* promotion rules, thereby not causing undefined behavior. The
* result is checked against the limits of T and true is returned if
* they are exceeded. Otherwise the sum is saved in result and false
* is returned.
*
* @return true on overflow, false on no overflow
*
* @param[in] summand_1, summand_2 The summands with are added
* @param[out] result Result of the addition, only populated when no
* overflow occurs.
*
* Further information:
* https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules
*/
template <typename T>
typename enable_if<is_signed<T>::VALUE && sizeof(T) < sizeof(int), bool>::type fallback_add_overflow(
T summand_1, T summand_2, T& result)
{
const int res = summand_1 + summand_2;
if ((res > std::numeric_limits<T>::max()) || (res < std::numeric_limits<T>::min())) {
return true;
} else {
result = static_cast<T>(res);
return false;
}
}
/*!
* @brief Check the addition of two numbers for overflows for unsigned
* integer types.
*
* This function adds summand_1 and summand_2 and checks after that if
* the operation overflowed. Since these are unsigned integers, no
* undefined behavior is invoked.
*
* @return true on overflow, false on no overflow
*
* @param[in] summand_1, summand_2 The summands with are added
* @param[out] result Result of the addition
*
* Further information:
* https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
*/
template <typename T>
typename enable_if<!is_signed<T>::VALUE, bool>::type fallback_add_overflow(T summand_1, T summand_2, T& result)
{
result = summand_1 + summand_2;
return result < summand_1;
}
/*!
* @brief Overflow addition check using compiler intrinsics.
*
* This function behaves exactly like fallback_add_overflow() but it
* relies on compiler intrinsics instead. This version should be faster
* than the fallback version as it can fully utilize available CPU
* instructions & the compiler's diagnostic.
*
* However, as some compilers don't provide intrinsics for certain
* types, the default implementation is the version from fallback.
*
* This function is fully specialized for each compiler.
*/
template <typename T>
bool builtin_add_overflow(T summand_1, T summand_2, T& result)
{
return fallback_add_overflow(summand_1, summand_2, result);
}
#if defined(__GNUC__) || defined(__clang__)
#if __GNUC__ >= 5 || __clang_major__ >= 3
/*!
* This macro pastes a specialization of builtin_add_overflow using gcc's &
* clang's __builtin_(s/u)add(l)(l)_overlow()
*
* The add function is implemented by forwarding the parameters to the intrinsic
* and returning its value.
*
* The intrinsics are documented here:
* https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html#Integer-Overflow-Builtins
*/
#define SPECIALIZE_builtin_add_overflow(type, builtin_name) \
/* Full specialization of builtin_add_overflow for type using the */ \
/* builtin_name intrinsic */ \
template <> \
inline bool builtin_add_overflow<type>(type summand_1, type summand_2, type & result) \
{ \
return builtin_name(summand_1, summand_2, &result); \
}
SPECIALIZE_builtin_add_overflow(int, __builtin_sadd_overflow);
SPECIALIZE_builtin_add_overflow(long, __builtin_saddl_overflow);
SPECIALIZE_builtin_add_overflow(long long, __builtin_saddll_overflow);
SPECIALIZE_builtin_add_overflow(unsigned int, __builtin_uadd_overflow);
SPECIALIZE_builtin_add_overflow(unsigned long, __builtin_uaddl_overflow);
SPECIALIZE_builtin_add_overflow(unsigned long long, __builtin_uaddll_overflow);
#undef SPECIALIZE_builtin_add_overflow
#endif // __GNUC__ >= 5 || __clang_major >= 3
#elif defined(_MSC_VER)
// intrinsics are not in available in MSVC 2005 and earlier
#if _MSC_VER >= 1400
/*!
* This macro pastes a specialization of builtin_add_overflow using MSVC's
* U(Int/Long/LongLong)Add.
*
* The add function is implemented by forwarding the parameters to the
* intrinsic. As MSVC's intrinsics return S_OK on success, this specialization
* returns whether the intrinsics return value does not equal S_OK. This ensures
* a uniform interface of the add function (false is returned when no overflow
* occurs, true on overflow).
*
* The intrinsics are documented here:
* https://msdn.microsoft.com/en-us/library/windows/desktop/ff516460(v=vs.85).aspx
*/
#define SPECIALIZE_builtin_add_overflow_WIN(type, builtin_name) \
template <> \
inline bool builtin_add_overflow(type summand_1, type summand_2, type& result) \
{ \
return builtin_name(summand_1, summand_2, &result) != S_OK; \
}
SPECIALIZE_builtin_add_overflow_WIN(unsigned int, UIntAdd);
SPECIALIZE_builtin_add_overflow_WIN(unsigned long, ULongAdd);
SPECIALIZE_builtin_add_overflow_WIN(unsigned long long, ULongLongAdd);
#undef SPECIALIZE_builtin_add_overflow_WIN
#endif // _MSC_VER >= 1400
#endif // defined(_MSC_VER)
} // namespace Internal
/*!
* @brief Safe addition, throws an exception on overflow.
*
* This function returns the result of summand_1 and summand_2 only when the
* operation would not overflow, otherwise an exception of type
* std::overflow_error is thrown.
*
* @param[in] summand_1, summand_2 summands to be summed up
* @return the sum of summand_1 and summand_2
* @throws std::overflow_error if the addition would overflow
*
* This function utilizes compiler builtins when available and should have a
* very small performance hit then. When builtins are unavailable, a more
* extensive check is required.
*
* Builtins are available for the following configurations:
* - GCC/Clang for signed and unsigned int, long and long long (not char & short)
* - MSVC for unsigned int, long and long long
*/
template <typename T>
T add(T summand_1, T summand_2)
{
T res = 0;
if (Internal::builtin_add_overflow(summand_1, summand_2, res)) {
throw std::overflow_error("Overflow in addition");
}
return res;
}
/*!
* @brief Calculates the absolute value of a number without producing
* negative values.
*
* The "standard" implementation of `abs(num)` (`num < 0 ? -num : num`)
* produces negative values when `num` is the smallest negative number. This
* is caused by `-1 * INTMAX = INTMIN + 1`, i.e. the real result of
* `abs(INTMIN)` overflows the integer type and results in `INTMIN` again
* (this is not guaranteed as it invokes undefined behavior).
*
* This function does not exhibit this behavior, it returns
* `std::numeric_limits<T>::max()` when the input is
* `std::numeric_limits<T>::min()`. The downside of this is that two
* negative values produce the same absolute value:
* `std::numeric_limits<T>::min()` and `std::numeric_limits<T>::min() + 1`.
*
* @tparam T a signed integer type
* @param[in] num The number which absolute value should be computed.
* @throws Never throws an exception.
* @return The absolute value of `num` or `std::numeric_limits<T>::max()`
* when `num == std::numeric_limits<T>::min()`.
*/
template <typename T>
typename Internal::enable_if<Internal::is_signed<T>::VALUE, T>::type abs(T num) throw()
{
if (num == std::numeric_limits<T>::min()) {
return std::numeric_limits<T>::max();
}
return num < 0 ? -num : num;
}
} // namespace Safe
#endif // SAFE_OP_HPP_