OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
7 | 7 |
8 #include <stddef.h> | 8 #include <stddef.h> |
9 #include <stdint.h> | 9 #include <stdint.h> |
10 | 10 |
11 #include <climits> | 11 #include <climits> |
12 #include <cmath> | 12 #include <cmath> |
13 #include <cstdlib> | 13 #include <cstdlib> |
14 #include <limits> | 14 #include <limits> |
15 #include <type_traits> | 15 #include <type_traits> |
16 | 16 |
17 #include "base/numerics/safe_conversions.h" | 17 #include "base/numerics/safe_conversions.h" |
18 | 18 |
19 namespace base { | 19 namespace base { |
20 namespace internal { | 20 namespace internal { |
21 | 21 |
22 // Everything from here up to the floating point operations is portable C++, | 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 | 23 // but it may not be fast. This code could be split based on |
24 // platform/architecture and replaced with potentially faster implementations. | 24 // platform/architecture and replaced with potentially faster implementations. |
25 | 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 static_assert(sizeof(uintmax_t) == 8, | |
61 "Max integer size not supported for this toolchain."); | |
62 }; | |
63 | |
64 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to | |
65 // support 128-bit math, then the ArithmeticPromotion template below will need | |
66 // to be updated (or more likely replaced with a decltype expression). | |
67 | |
68 template <typename Integer> | |
69 struct UnsignedIntegerForSize { | |
70 typedef typename std::enable_if< | |
71 std::numeric_limits<Integer>::is_integer, | |
72 typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type; | |
73 }; | |
74 | |
75 template <typename Integer> | |
76 struct SignedIntegerForSize { | |
77 typedef typename std::enable_if< | |
78 std::numeric_limits<Integer>::is_integer, | |
79 typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type; | |
80 }; | |
81 | |
82 template <typename Integer> | |
83 struct TwiceWiderInteger { | |
84 typedef typename std::enable_if< | |
85 std::numeric_limits<Integer>::is_integer, | |
86 typename IntegerForSizeAndSign< | |
87 sizeof(Integer) * 2, | |
88 std::numeric_limits<Integer>::is_signed>::type>::type type; | |
89 }; | |
90 | |
91 template <typename Integer> | |
92 struct PositionOfSignBit { | |
93 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, | |
94 size_t>::type value = | |
95 CHAR_BIT * sizeof(Integer) - 1; | |
96 }; | |
97 | |
98 // This is used for UnsignedAbs, where we need to support floating-point | 26 // This is used for UnsignedAbs, where we need to support floating-point |
99 // template instantiations even though we don't actually support the operations. | 27 // template instantiations even though we don't actually support the operations. |
100 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, | 28 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, |
101 // so the float versions will not compile. | 29 // so the float versions will not compile. |
102 template <typename Numeric, | 30 template <typename Numeric, |
103 bool IsInteger = std::numeric_limits<Numeric>::is_integer, | 31 bool IsInteger = std::numeric_limits<Numeric>::is_integer, |
104 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> | 32 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> |
105 struct UnsignedOrFloatForSize; | 33 struct UnsignedOrFloatForSize; |
106 | 34 |
107 template <typename Numeric> | 35 template <typename Numeric> |
(...skipping 14 matching lines...) Expand all Loading... |
122 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> | 50 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> |
123 PositionOfSignBit<T>::value); | 51 PositionOfSignBit<T>::value); |
124 } | 52 } |
125 | 53 |
126 // This wrapper undoes the standard integer promotions. | 54 // This wrapper undoes the standard integer promotions. |
127 template <typename T> | 55 template <typename T> |
128 constexpr T BinaryComplement(T x) { | 56 constexpr T BinaryComplement(T x) { |
129 return static_cast<T>(~x); | 57 return static_cast<T>(~x); |
130 } | 58 } |
131 | 59 |
132 enum ArithmeticPromotionCategory { | |
133 LEFT_PROMOTION, // Use the type of the left-hand argument. | |
134 RIGHT_PROMOTION, // Use the type of the right-hand argument. | |
135 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent. | |
136 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type. | |
137 }; | |
138 | |
139 template <ArithmeticPromotionCategory Promotion, | |
140 typename Lhs, | |
141 typename Rhs = Lhs> | |
142 struct ArithmeticPromotion; | |
143 | |
144 template <typename Lhs, | |
145 typename Rhs, | |
146 ArithmeticPromotionCategory Promotion = | |
147 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) | |
148 ? LEFT_PROMOTION | |
149 : RIGHT_PROMOTION> | |
150 struct MaxExponentPromotion; | |
151 | |
152 template <typename Lhs, typename Rhs> | |
153 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { | |
154 using type = Lhs; | |
155 }; | |
156 | |
157 template <typename Lhs, typename Rhs> | |
158 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { | |
159 using type = Rhs; | |
160 }; | |
161 | |
162 template <typename Lhs, | |
163 typename Rhs = Lhs, | |
164 bool is_intmax_type = | |
165 std::is_integral< | |
166 typename MaxExponentPromotion<Lhs, Rhs>::type>::value && | |
167 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) == | |
168 sizeof(intmax_t), | |
169 bool is_max_exponent = | |
170 StaticDstRangeRelationToSrcRange< | |
171 typename MaxExponentPromotion<Lhs, Rhs>::type, | |
172 Lhs>::value == | |
173 NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< | |
174 typename MaxExponentPromotion<Lhs, Rhs>::type, | |
175 Rhs>::value == NUMERIC_RANGE_CONTAINED> | |
176 struct BigEnoughPromotion; | |
177 | |
178 // The side with the max exponent is big enough. | |
179 template <typename Lhs, typename Rhs, bool is_intmax_type> | |
180 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { | |
181 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | |
182 static const bool is_contained = true; | |
183 }; | |
184 | |
185 // We can use a twice wider type to fit. | |
186 template <typename Lhs, typename Rhs> | |
187 struct BigEnoughPromotion<Lhs, Rhs, false, false> { | |
188 using type = typename IntegerForSizeAndSign< | |
189 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) * 2, | |
190 std::is_signed<Lhs>::value || std::is_signed<Rhs>::value>::type; | |
191 static const bool is_contained = true; | |
192 }; | |
193 | |
194 // No type is large enough. | |
195 template <typename Lhs, typename Rhs> | |
196 struct BigEnoughPromotion<Lhs, Rhs, true, false> { | |
197 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | |
198 static const bool is_contained = false; | |
199 }; | |
200 | |
201 // These are the supported promotion types. | |
202 | |
203 // Use the type supporting the largest exponent. | |
204 template <typename Lhs, typename Rhs> | |
205 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> { | |
206 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | |
207 static const bool is_contained = true; | |
208 }; | |
209 | |
210 // Attempt to find a big enough type. | |
211 template <typename Lhs, typename Rhs> | |
212 struct ArithmeticPromotion<BIG_ENOUGH_PROMOTION, Lhs, Rhs> { | |
213 using type = typename BigEnoughPromotion<Lhs, Rhs>::type; | |
214 static const bool is_contained = BigEnoughPromotion<Lhs, Rhs>::is_contained; | |
215 }; | |
216 | |
217 // We can statically check if operations on the provided types can wrap, so we | |
218 // can skip the checked operations if they're not needed. So, for an integer we | |
219 // care if the destination type preserves the sign and is twice the width of | |
220 // the source. | |
221 template <typename T, typename Lhs, typename Rhs> | |
222 struct IsIntegerArithmeticSafe { | |
223 static const bool value = !std::numeric_limits<T>::is_iec559 && | |
224 StaticDstRangeRelationToSrcRange<T, Lhs>::value == | |
225 NUMERIC_RANGE_CONTAINED && | |
226 sizeof(T) >= (2 * sizeof(Lhs)) && | |
227 StaticDstRangeRelationToSrcRange<T, Rhs>::value != | |
228 NUMERIC_RANGE_CONTAINED && | |
229 sizeof(T) >= (2 * sizeof(Rhs)); | |
230 }; | |
231 | |
232 // Probe for builtin math overflow support on Clang and version check on GCC. | 60 // Probe for builtin math overflow support on Clang and version check on GCC. |
233 #if defined(__has_builtin) | 61 #if defined(__has_builtin) |
234 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) | 62 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) |
235 #elif defined(__GNUC__) | 63 #elif defined(__GNUC__) |
236 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) | 64 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) |
237 #else | 65 #else |
238 #define USE_OVERFLOW_BUILTINS (0) | 66 #define USE_OVERFLOW_BUILTINS (0) |
239 #endif | 67 #endif |
240 | 68 |
241 // Here are the actual portable checked integer math implementations. | 69 // Here are the actual portable checked integer math implementations. |
(...skipping 623 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
865 | 693 |
866 // Copy constructor. | 694 // Copy constructor. |
867 template <typename Src> | 695 template <typename Src> |
868 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) | 696 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) |
869 : value_(static_cast<T>(rhs.value())) {} | 697 : value_(static_cast<T>(rhs.value())) {} |
870 | 698 |
871 constexpr bool is_valid() const { return std::isfinite(value_); } | 699 constexpr bool is_valid() const { return std::isfinite(value_); } |
872 constexpr T value() const { return value_; } | 700 constexpr T value() const { return value_; } |
873 }; | 701 }; |
874 | 702 |
875 // The following are helper templates used in the CheckedNumeric class. | |
876 template <typename T> | |
877 class CheckedNumeric; | |
878 | |
879 // Used to treat CheckedNumeric and arithmetic underlying types the same. | |
880 template <typename T> | |
881 struct UnderlyingType { | |
882 using type = T; | |
883 static const bool is_numeric = std::is_arithmetic<T>::value; | |
884 static const bool is_checked = false; | |
885 }; | |
886 | |
887 template <typename T> | |
888 struct UnderlyingType<CheckedNumeric<T>> { | |
889 using type = T; | |
890 static const bool is_numeric = true; | |
891 static const bool is_checked = true; | |
892 }; | |
893 | |
894 template <typename L, typename R> | |
895 struct IsCheckedOp { | |
896 static const bool value = | |
897 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && | |
898 (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); | |
899 }; | |
900 | |
901 template <template <typename, typename, typename> class M, | 703 template <template <typename, typename, typename> class M, |
902 typename L, | 704 typename L, |
903 typename R> | 705 typename R> |
904 struct MathWrapper { | 706 struct MathWrapper { |
905 using math = M<typename UnderlyingType<L>::type, | 707 using math = M<typename UnderlyingType<L>::type, |
906 typename UnderlyingType<R>::type, | 708 typename UnderlyingType<R>::type, |
907 void>; | 709 void>; |
908 using type = typename math::result_type; | 710 using type = typename math::result_type; |
909 }; | 711 }; |
910 | 712 |
911 } // namespace internal | 713 } // namespace internal |
912 } // namespace base | 714 } // namespace base |
913 | 715 |
914 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 716 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
OLD | NEW |