webkit  2cdf99a9e3038c7e01b3c37e8ad903ecbe5eecf1
https://github.com/WebKit/webkit
safe_math_impl.h
Go to the documentation of this file.
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <climits>
12 #include <cmath>
13 #include <cstdlib>
14 #include <limits>
15 #include <type_traits>
16 
18 
19 namespace base
20 {
21 namespace internal
22 {
23 
24 // Everything from here up to the floating point operations is portable C++,
25 // but it may not be fast. This code could be split based on
26 // platform/architecture and replaced with potentially faster implementations.
27 
28 // Integer promotion templates used by the portable checked integer arithmetic.
29 template <size_t Size, bool IsSigned>
31 template <>
33 {
34  typedef int8_t type;
35 };
36 template <>
38 {
39  typedef uint8_t type;
40 };
41 template <>
43 {
44  typedef int16_t type;
45 };
46 template <>
48 {
49  typedef uint16_t type;
50 };
51 template <>
53 {
54  typedef int32_t type;
55 };
56 template <>
58 {
59  typedef uint32_t type;
60 };
61 template <>
63 {
64  typedef int64_t type;
65 };
66 template <>
68 {
69  typedef uint64_t type;
70 };
71 
72 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
73 // support 128-bit math, then the ArithmeticPromotion template below will need
74 // to be updated (or more likely replaced with a decltype expression).
75 
76 template <typename Integer>
78 {
79  typedef
80  typename std::enable_if<std::numeric_limits<Integer>::is_integer,
83 };
84 
85 template <typename Integer>
87 {
88  typedef
89  typename std::enable_if<std::numeric_limits<Integer>::is_integer,
92 };
93 
94 template <typename Integer>
96 {
97  typedef typename std::enable_if<
98  std::numeric_limits<Integer>::is_integer,
99  typename IntegerForSizeAndSign<sizeof(Integer) * 2,
100  std::numeric_limits<Integer>::is_signed>::type>::type type;
101 };
102 
103 template <typename Integer>
105 {
106  static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, size_t>::type
107  value = CHAR_BIT * sizeof(Integer) - 1;
108 };
109 
110 // This is used for UnsignedAbs, where we need to support floating-point
111 // template instantiations even though we don't actually support the operations.
112 // However, there is no corresponding implementation of e.g. CheckedUnsignedAbs,
113 // so the float versions will not compile.
114 template <typename Numeric,
115  bool IsInteger = std::numeric_limits<Numeric>::is_integer,
116  bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
118 
119 template <typename Numeric>
121 {
123 };
124 
125 template <typename Numeric>
127 {
128  typedef Numeric type;
129 };
130 
131 // Helper templates for integer manipulations.
132 
133 template <typename T>
134 constexpr bool HasSignBit(T x)
135 {
136  // Cast to unsigned since right shift on signed is undefined.
137  return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
139 }
140 
141 // This wrapper undoes the standard integer promotions.
142 template <typename T>
143 constexpr T BinaryComplement(T x)
144 {
145  return static_cast<T>(~x);
146 }
147 
148 // Here are the actual portable checked integer math implementations.
149 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
150 // way to coalesce things into the CheckedNumericState specializations below.
151 
152 template <typename T>
153 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
155 {
156  // Since the value of x+y is undefined if we have a signed type, we compute
157  // it using the unsigned type of the same size.
158  typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
159  UnsignedDst ux = static_cast<UnsignedDst>(x);
160  UnsignedDst uy = static_cast<UnsignedDst>(y);
161  UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
162  // Addition is valid if the sign of (x + y) is equal to either that of x or
163  // that of y.
164  if (std::numeric_limits<T>::is_signed)
165  {
166  if (HasSignBit(BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))))
167  {
168  *validity = RANGE_VALID;
169  }
170  else
171  { // Direction of wrap is inverse of result sign.
172  *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
173  }
174  }
175  else
176  { // Unsigned is either valid or overflow.
177  *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW;
178  }
179  return static_cast<T>(uresult);
180 }
181 
182 template <typename T>
183 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
185 {
186  // Since the value of x+y is undefined if we have a signed type, we compute
187  // it using the unsigned type of the same size.
188  typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
189  UnsignedDst ux = static_cast<UnsignedDst>(x);
190  UnsignedDst uy = static_cast<UnsignedDst>(y);
191  UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
192  // Subtraction is valid if either x and y have same sign, or (x-y) and x have
193  // the same sign.
194  if (std::numeric_limits<T>::is_signed)
195  {
196  if (HasSignBit(BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))))
197  {
198  *validity = RANGE_VALID;
199  }
200  else
201  { // Direction of wrap is inverse of result sign.
202  *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
203  }
204  }
205  else
206  { // Unsigned is either valid or underflow.
207  *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW;
208  }
209  return static_cast<T>(uresult);
210 }
211 
212 // Integer multiplication is a bit complicated. In the fast case we just
213 // we just promote to a twice wider type, and range check the result. In the
214 // slow case we need to manually check that the result won't be truncated by
215 // checking with division against the appropriate bound.
216 template <typename T>
217 typename std::enable_if<std::numeric_limits<T>::is_integer && sizeof(T) * 2 <= sizeof(uintmax_t),
218  T>::type
220 {
221  typedef typename TwiceWiderInteger<T>::type IntermediateType;
222  IntermediateType tmp = static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
223  *validity = DstRangeRelationToSrcRange<T>(tmp);
224  return static_cast<T>(tmp);
225 }
226 
227 template <typename T>
228 typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed &&
229  (sizeof(T) * 2 > sizeof(uintmax_t)),
230  T>::type
231 CheckedMul(T x, T y, RangeConstraint *validity)
232 {
233  // If either side is zero then the result will be zero.
234  if (!x || !y)
235  {
236  *validity = RANGE_VALID;
237  return static_cast<T>(0);
238  }
239  else if (x > 0)
240  {
241  if (y > 0)
243  else
244  *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID : RANGE_UNDERFLOW;
245  }
246  else
247  {
248  if (y > 0)
249  *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID : RANGE_UNDERFLOW;
250  else
252  }
253 
254  return static_cast<T>(x * y);
255 }
256 
257 template <typename T>
258 typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed &&
259  (sizeof(T) * 2 > sizeof(uintmax_t)),
260  T>::type
261 CheckedMul(T x, T y, RangeConstraint *validity)
262 {
263  *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y) ? RANGE_VALID : RANGE_OVERFLOW;
264  return static_cast<T>(x * y);
265 }
266 
267 // Division just requires a check for an invalid negation on signed min/-1.
268 template <typename T>
270  T y,
271  RangeConstraint *validity,
272  typename std::enable_if<std::numeric_limits<T>::is_integer, int>::type = 0)
273 {
274  if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
275  y == static_cast<T>(-1))
276  {
277  *validity = RANGE_OVERFLOW;
279  }
280 
281  *validity = RANGE_VALID;
282  return static_cast<T>(x / y);
283 }
284 
285 template <typename T>
286 typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
287  T>::type
288 CheckedMod(T x, T y, RangeConstraint *validity)
289 {
290  *validity = y > 0 ? RANGE_VALID : RANGE_INVALID;
291  return static_cast<T>(x % y);
292 }
293 
294 template <typename T>
295 typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
296  T>::type
297 CheckedMod(T x, T y, RangeConstraint *validity)
298 {
299  *validity = RANGE_VALID;
300  return static_cast<T>(x % y);
301 }
302 
303 template <typename T>
304 typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
305  T>::type
307 {
308  *validity = value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
309  // The negation of signed min is min, so catch that one.
310  return static_cast<T>(-value);
311 }
312 
313 template <typename T>
314 typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
315  T>::type
317 {
318  // The only legal unsigned negation is zero.
319  *validity = value ? RANGE_UNDERFLOW : RANGE_VALID;
320  return static_cast<T>(-static_cast<typename SignedIntegerForSize<T>::type>(value));
321 }
322 
323 template <typename T>
324 typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
325  T>::type
327 {
328  *validity = value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
329  return static_cast<T>(std::abs(value));
330 }
331 
332 template <typename T>
333 typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
334  T>::type
336 {
337  // T is unsigned, so |value| must already be positive.
338  *validity = RANGE_VALID;
339  return value;
340 }
341 
342 template <typename T>
343 typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
346 {
347  typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
348  return value == std::numeric_limits<T>::min()
349  ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
350  : static_cast<UnsignedT>(std::abs(value));
351 }
352 
353 template <typename T>
354 typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
355  T>::type
357 {
358  // T is unsigned, so |value| must already be positive.
359  return static_cast<T>(value);
360 }
361 
362 // These are the floating point stubs that the compiler needs to see. Only the
363 // negation operation is ever called.
364 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
365  template <typename T> \
366  typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type Checked##NAME( \
367  T, T, RangeConstraint *) \
368  { \
369  NOTREACHED(); \
370  return static_cast<T>(0); \
371  }
372 
378 
379 #undef BASE_FLOAT_ARITHMETIC_STUBS
380 
381 template <typename T>
382 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(T value,
383  RangeConstraint *)
384 {
385  return static_cast<T>(-value);
386 }
387 
388 template <typename T>
389 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(T value,
390  RangeConstraint *)
391 {
392  return static_cast<T>(std::abs(value));
393 }
394 
395 // Floats carry around their validity state with them, but integers do not. So,
396 // we wrap the underlying value in a specialization in order to hide that detail
397 // and expose an interface via accessors.
399 {
403 };
404 
405 template <typename NumericType>
407 {
409  std::numeric_limits<NumericType>::is_integer
411  : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING : NUMERIC_UNKNOWN);
412 };
413 
416 {
417 };
418 
419 // Integrals require quite a bit of additional housekeeping to manage state.
420 template <typename T>
422 {
423  private:
424  T value_;
425  RangeConstraint validity_ : CHAR_BIT; // Actually requires only two bits.
426 
427  public:
428  template <typename Src, NumericRepresentation type>
429  friend class CheckedNumericState;
430 
431  CheckedNumericState() : value_(0), validity_(RANGE_VALID) {}
432 
433  template <typename Src>
435  : value_(static_cast<T>(value)),
436  validity_(GetRangeConstraint(validity | DstRangeRelationToSrcRange<T>(value)))
437  {
438  static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric.");
439  }
440 
441  // Copy constructor.
442  template <typename Src>
444  : value_(static_cast<T>(rhs.value())),
445  validity_(GetRangeConstraint(rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value())))
446  {
447  }
448 
449  template <typename Src>
451  Src value,
452  typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0)
453  : value_(static_cast<T>(value)), validity_(DstRangeRelationToSrcRange<T>(value))
454  {
455  }
456 
457  RangeConstraint validity() const { return validity_; }
458  T value() const { return value_; }
459 };
460 
461 // Floating points maintain their own validity, but need translation wrappers.
462 template <typename T>
464 {
465  private:
466  T value_;
467 
468  public:
469  template <typename Src, NumericRepresentation type>
470  friend class CheckedNumericState;
471 
472  CheckedNumericState() : value_(0.0) {}
473 
474  template <typename Src>
476  Src value,
477  RangeConstraint validity,
478  typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type = 0)
479  {
480  switch (DstRangeRelationToSrcRange<T>(value))
481  {
482  case RANGE_VALID:
483  value_ = static_cast<T>(value);
484  break;
485 
486  case RANGE_UNDERFLOW:
487  value_ = -std::numeric_limits<T>::infinity();
488  break;
489 
490  case RANGE_OVERFLOW:
491  value_ = std::numeric_limits<T>::infinity();
492  break;
493 
494  case RANGE_INVALID:
495  value_ = std::numeric_limits<T>::quiet_NaN();
496  break;
497 
498  default:
499  NOTREACHED();
500  }
501  }
502 
503  template <typename Src>
505  Src value,
506  typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0)
507  : value_(static_cast<T>(value))
508  {
509  }
510 
511  // Copy constructor.
512  template <typename Src>
513  CheckedNumericState(const CheckedNumericState<Src> &rhs) : value_(static_cast<T>(rhs.value()))
514  {
515  }
516 
518  {
520  value_ >= -std::numeric_limits<T>::max());
521  }
522  T value() const { return value_; }
523 };
524 
525 // For integers less than 128-bit and floats 32-bit or larger, we have the type
526 // with the larger maximum exponent take precedence.
528 {
531 };
532 
533 template <typename Lhs,
534  typename Rhs = Lhs,
535  ArithmeticPromotionCategory Promotion =
537  : RIGHT_PROMOTION>
539 
540 template <typename Lhs, typename Rhs>
542 {
543  typedef Lhs type;
544 };
545 
546 template <typename Lhs, typename Rhs>
548 {
549  typedef Rhs type;
550 };
551 
552 // We can statically check if operations on the provided types can wrap, so we
553 // can skip the checked operations if they're not needed. So, for an integer we
554 // care if the destination type preserves the sign and is twice the width of
555 // the source.
556 template <typename T, typename Lhs, typename Rhs>
558 {
559  static const bool value =
560  !std::numeric_limits<T>::is_iec559 &&
562  sizeof(T) >= (2 * sizeof(Lhs)) &&
564  sizeof(T) >= (2 * sizeof(Rhs));
565 };
566 
567 } // namespace internal
568 } // namespace base
569 
570 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
Definition: safe_conversions_impl.h:98
Definition: safe_math_impl.h:104
std::enable_if< std::numeric_limits< T >::is_integer, T >::type CheckedSub(T x, T y, RangeConstraint *validity)
Definition: safe_math_impl.h:184
#define BASE_FLOAT_ARITHMETIC_STUBS(NAME)
Definition: safe_math_impl.h:364
RangeConstraint
Definition: safe_conversions_impl.h:95
unsigned long long uint64_t
Definition: ptypes.h:120
Definition: safe_math_impl.h:415
uint64_t type
Definition: safe_math_impl.h:69
constexpr RangeConstraint DstRangeRelationToSrcRange(Src value)
Definition: safe_conversions_impl.h:259
int32_t type
Definition: safe_math_impl.h:54
signed int int32_t
Definition: ptypes.h:101
Definition: safe_math_impl.h:401
unsigned int uint32_t
Definition: ptypes.h:105
CheckedNumericState(Src value, typename std::enable_if< std::numeric_limits< Src >::is_specialized, int >::type=0)
Definition: safe_math_impl.h:450
#define NOTREACHED()
Definition: logging.h:19
std::enable_if< std::numeric_limits< T >::is_integer &&std::numeric_limits< T >::is_signed, T >::type CheckedMod(T x, T y, RangeConstraint *validity)
Definition: safe_math_impl.h:288
std::enable_if< std::numeric_limits< T >::is_integer &&!std::numeric_limits< T >::is_signed &&(sizeof(T) *2 > sizeof(uintmax_t)), T >::type CheckedMul(T x, T y, RangeConstraint *validity)
Definition: safe_math_impl.h:261
else * validity
Definition: safe_math_impl.h:251
NumericRepresentation
Definition: safe_math_impl.h:398
return static_cast< T >(x *y)
bool IsInteger(TBasicType type)
Definition: BaseTypes.h:279
T CheckedDiv(T x, T y, RangeConstraint *validity, typename std::enable_if< std::numeric_limits< T >::is_integer, int >::type=0)
Definition: safe_math_impl.h:269
signed long long int64_t
Definition: ptypes.h:112
std::enable_if< std::numeric_limits< T >::is_integer, T >::type CheckedAdd(T x, T y, RangeConstraint *validity)
Definition: safe_math_impl.h:154
signed short int16_t
Definition: ptypes.h:93
int16_t type
Definition: safe_math_impl.h:44
uint8_t type
Definition: safe_math_impl.h:39
RangeConstraint validity() const
Definition: safe_math_impl.h:517
Definition: safe_math_impl.h:402
Definition: safe_math_impl.h:30
CheckedNumericState(Src value, typename std::enable_if< std::numeric_limits< Src >::is_specialized, int >::type=0)
Definition: safe_math_impl.h:504
RangeConstraint validity() const
Definition: safe_math_impl.h:457
std::enable_if< std::numeric_limits< T >::is_integer &&std::numeric_limits< T >::is_signed, T >::type CheckedAbs(T value, RangeConstraint *validity)
Definition: safe_math_impl.h:326
std::enable_if< std::numeric_limits< T >::is_integer &&std::numeric_limits< T >::is_signed, T >::type CheckedNeg(T value, RangeConstraint *validity)
Definition: safe_math_impl.h:306
TestSubObjConstructor T
Definition: TestTypedefs.idl:84
CheckedNumericState(const CheckedNumericState< Src > &rhs)
Definition: safe_math_impl.h:513
EGLSurface EGLint x
Definition: eglext.h:950
Definition: safe_conversions_impl.h:23
EGLAttrib * value
Definition: eglext.h:120
std::enable_if< std::numeric_limits< Integer >::is_integer, typename IntegerForSizeAndSign< sizeof(Integer), true >::type >::type type
Definition: safe_math_impl.h:91
unsigned char uint8_t
Definition: ptypes.h:89
Definition: safe_math_impl.h:406
Definition: safe_math_impl.h:400
std::enable_if< std::numeric_limits< T >::is_integer &&std::numeric_limits< T >::is_signed, typename UnsignedIntegerForSize< T >::type >::type CheckedUnsignedAbs(T value)
Definition: safe_math_impl.h:345
unsigned short uint16_t
Definition: ptypes.h:97
uint32_t type
Definition: safe_math_impl.h:59
UnsignedIntegerForSize< Numeric >::type type
Definition: safe_math_impl.h:122
EGLSurface EGLint EGLint y
Definition: eglext.h:950
Definition: safe_math_impl.h:86
#define true
Definition: float-mm.c:6
uint16_t type
Definition: safe_math_impl.h:49
Definition: safe_math_impl.h:538
Definition: safe_math_impl.h:530
CheckedNumericState()
Definition: safe_math_impl.h:472
Definition: safe_conversions.h:16
EGLenum type
Definition: eglext.h:63
Definition: document.h:393
Definition: safe_math_impl.h:557
double max
Definition: DeviceProximityEvent.idl:32
#define min(a, b)
Definition: user_environment.h:66
uint64_t uintmax_t
Definition: stdint.h:166
CheckedNumericState(Src value, RangeConstraint validity)
Definition: safe_math_impl.h:434
#define false
Definition: float-mm.c:5
int8_t type
Definition: safe_math_impl.h:34
constexpr bool HasSignBit(T x)
Definition: safe_math_impl.h:134
T value() const
Definition: safe_math_impl.h:522
Definition: safe_math_impl.h:117
Definition: safe_conversions_impl.h:46
Definition: safe_conversions_impl.h:97
signed char int8_t
Definition: ptypes.h:85
Definition: safe_conversions_impl.h:100
Definition: safe_conversions_impl.h:99
CheckedNumericState(Src value, RangeConstraint validity, typename std::enable_if< std::numeric_limits< Src >::is_integer, int >::type=0)
Definition: safe_math_impl.h:475
constexpr T BinaryComplement(T x)
Definition: safe_math_impl.h:143
T value() const
Definition: safe_math_impl.h:458
Definition: safe_math_impl.h:77
constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint)
Definition: safe_conversions_impl.h:104
int64_t type
Definition: safe_math_impl.h:64
ArithmeticPromotionCategory
Definition: safe_math_impl.h:527
std::enable_if< std::numeric_limits< Integer >::is_integer, typename IntegerForSizeAndSign< sizeof(Integer) *2, std::numeric_limits< Integer >::is_signed >::type >::type type
Definition: safe_math_impl.h:100
#define abs(a)
Definition: device_info_impl.cc:19
Definition: safe_conversions_impl.h:60
#define T(a)
Definition: row_common.cc:1964
std::enable_if< std::numeric_limits< Integer >::is_integer, typename IntegerForSizeAndSign< sizeof(Integer), false >::type >::type type
Definition: safe_math_impl.h:82
CheckedNumericState()
Definition: safe_math_impl.h:431
Definition: safe_math_impl.h:529
CheckedNumericState(const CheckedNumericState< Src > &rhs)
Definition: safe_math_impl.h:443
Definition: safe_math_impl.h:95