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