Exiv2
safe_op.hpp
1 // ***************************************************************** -*- C++ -*-
2 /*
3  * Copyright (C) 2004-2021 Exiv2 authors
4  * This program is part of the Exiv2 distribution.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #ifndef SAFE_OP_HPP_
22 #define SAFE_OP_HPP_
23 
24 #include <limits>
25 #include <stdexcept>
26 
27 #ifdef _MSC_VER
28 #include <Intsafe.h>
29 #endif
30 
34 namespace Safe
35 {
58  namespace Internal
59  {
67  template <typename T>
68  struct is_signed
69  {
70  enum
71  {
72  VALUE = T(-1) < T(0)
73  };
74  };
75 
82  template <bool B, class T = void>
83  struct enable_if
84  {
85  };
86 
90  template <class T>
91  struct enable_if<true, T>
92  {
93  typedef T type;
94  };
95 
113  template <typename T>
114  typename enable_if<is_signed<T>::VALUE && sizeof(T) >= sizeof(int), bool>::type fallback_add_overflow(
115  T summand_1, T summand_2, T& result)
116  {
117  if (((summand_2 >= 0) && (summand_1 > std::numeric_limits<T>::max() - summand_2)) ||
118  ((summand_2 < 0) && (summand_1 < std::numeric_limits<T>::min() - summand_2))) {
119  return true;
120  } else {
121  result = summand_1 + summand_2;
122  return false;
123  }
124  }
125 
145  template <typename T>
146  typename enable_if<is_signed<T>::VALUE && sizeof(T) < sizeof(int), bool>::type fallback_add_overflow(
147  T summand_1, T summand_2, T& result)
148  {
149  const int res = summand_1 + summand_2;
150  if ((res > std::numeric_limits<T>::max()) || (res < std::numeric_limits<T>::min())) {
151  return true;
152  } else {
153  result = static_cast<T>(res);
154  return false;
155  }
156  }
157 
174  template <typename T>
175  typename enable_if<!is_signed<T>::VALUE, bool>::type fallback_add_overflow(T summand_1, T summand_2, T& result)
176  {
177  result = summand_1 + summand_2;
178  return result < summand_1;
179  }
180 
194  template <typename T>
195  bool builtin_add_overflow(T summand_1, T summand_2, T& result)
196  {
197  return fallback_add_overflow(summand_1, summand_2, result);
198  }
199 
200 #if defined(__GNUC__) || defined(__clang__)
201 #if __GNUC__ >= 5 || __clang_major__ >= 3
202 
213 #define SPECIALIZE_builtin_add_overflow(type, builtin_name) \
214  /* Full specialization of builtin_add_overflow for type using the */ \
215  /* builtin_name intrinsic */ \
216  template <> \
217  inline bool builtin_add_overflow<type>(type summand_1, type summand_2, type & result) \
218  { \
219  return builtin_name(summand_1, summand_2, &result); \
220  }
221 
222  SPECIALIZE_builtin_add_overflow(int, __builtin_sadd_overflow);
223  SPECIALIZE_builtin_add_overflow(long, __builtin_saddl_overflow);
224  SPECIALIZE_builtin_add_overflow(long long, __builtin_saddll_overflow);
225 
226  SPECIALIZE_builtin_add_overflow(unsigned int, __builtin_uadd_overflow);
227  SPECIALIZE_builtin_add_overflow(unsigned long, __builtin_uaddl_overflow);
228  SPECIALIZE_builtin_add_overflow(unsigned long long, __builtin_uaddll_overflow);
229 
230 #undef SPECIALIZE_builtin_add_overflow
231 #endif // __GNUC__ >= 5 || __clang_major >= 3
232 
233 #elif defined(_MSC_VER)
234 // intrinsics are not in available in MSVC 2005 and earlier
235 #if _MSC_VER >= 1400
236 
250 #define SPECIALIZE_builtin_add_overflow_WIN(type, builtin_name) \
251  template <> \
252  inline bool builtin_add_overflow(type summand_1, type summand_2, type& result) \
253  { \
254  return builtin_name(summand_1, summand_2, &result) != S_OK; \
255  }
256 
257  SPECIALIZE_builtin_add_overflow_WIN(unsigned int, UIntAdd);
258  SPECIALIZE_builtin_add_overflow_WIN(unsigned long, ULongAdd);
259  SPECIALIZE_builtin_add_overflow_WIN(unsigned long long, ULongLongAdd);
260 
261 #undef SPECIALIZE_builtin_add_overflow_WIN
262 
263 #endif // _MSC_VER >= 1400
264 #endif // defined(_MSC_VER)
265 
266  } // namespace Internal
267 
287  template <typename T>
288  T add(T summand_1, T summand_2)
289  {
290  T res = 0;
291  if (Internal::builtin_add_overflow(summand_1, summand_2, res)) {
292  throw std::overflow_error("Overflow in addition");
293  }
294  return res;
295  }
296 
319  template <typename T>
320  typename Internal::enable_if<Internal::is_signed<T>::VALUE, T>::type abs(T num) throw()
321  {
322  if (num == std::numeric_limits<T>::min()) {
323  return std::numeric_limits<T>::max();
324  }
325  return num < 0 ? -num : num;
326  }
327 
328 } // namespace Safe
329 
330 #endif // SAFE_OP_HPP_
Arithmetic operations with overflow checks.
Definition: safe_op.hpp:35
T add(T summand_1, T summand_2)
Safe addition, throws an exception on overflow.
Definition: safe_op.hpp:288
Internal::enable_if< Internal::is_signed< T >::VALUE, T >::type abs(T num)
Calculates the absolute value of a number without producing negative values.
Definition: safe_op.hpp:320
Helper struct for SFINAE, from C++11.
Definition: safe_op.hpp:84
Helper struct to determine whether a type is signed or unsigned.
Definition: safe_op.hpp:69