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