Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(218)

Side by Side Diff: base/numerics/safe_conversions.h

Issue 2945433003: Add ClampedNumeric templates (Closed)
Patch Set: more docs and modulus Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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_CONVERSIONS_H_ 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_ 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_
7 7
8 #include <stddef.h> 8 #include <stddef.h>
9 9
10 #include <limits> 10 #include <limits>
11 #include <ostream> 11 #include <ostream>
12 #include <type_traits> 12 #include <type_traits>
13 13
14 #include "base/numerics/safe_conversions_impl.h" 14 #include "base/numerics/safe_conversions_impl.h"
15 15
16 namespace base { 16 namespace base {
17 17
18 // The following are helper constexpr template functions and classes for safely
19 // performing a range of conversions, assignments, and tests:
20 //
21 // checked_cast<> - Analogous to static_cast<> for numeric types, except
22 // that it CHECKs that the specified numeric conversion will not overflow
23 // or underflow. NaN source will always trigger a CHECK.
24 // The default CHECK triggers a crash, but the handler can be overriden.
25 // saturated_cast<> - Analogous to static_cast<> for numeric types, except
26 // that it returns a saturated result when the specified numeric conversion
27 // would otherwise overflow or underflow. An NaN source returns 0 by
28 // default, but can be overridden to return a different result.
29 // strict_cast<> - Analogous to static_cast<> for numeric types, except that
30 // it will cause a compile failure if the destination type is not large
31 // enough to contain any value in the source type. It performs no runtime
32 // checking and thus introduces no runtime overhead.
33 // IsValueInRangeForNumericType<>() - A convenience function that returns true
34 // if the type supplied to the template parameter can represent the value
35 // passed as an argument to the function.
36 // IsValueNegative<>() - A convenience function that will accept any arithmetic
37 // type as an argument and will return whether the value is less than zero.
38 // Unsigned types always return false.
39 // SafeUnsignedAbs() - Returns the absolute value of the supplied integer
40 // parameter as an unsigned result (thus avoiding an overflow if the value
41 // is the signed, two's complement minimum).
42 // StrictNumeric<> - A wrapper type that performs assignments and copies via
43 // the strict_cast<> template, and can perform valid arithmetic comparisons
44 // across any range of arithmetic types. StrictNumeric is the return type
45 // for values extracted from a CheckedNumeric class instance. The raw
46 // arithmetic value is extracted via static_cast to the underlying type.
47 // MakeStrictNum() - Creates a new StrictNumeric from the underlying type of
48 // the supplied arithmetic or StrictNumeric type.
49
50 // Convenience function that returns true if the supplied value is in range 18 // Convenience function that returns true if the supplied value is in range
51 // for the destination type. 19 // for the destination type.
52 template <typename Dst, typename Src> 20 template <typename Dst, typename Src>
53 constexpr bool IsValueInRangeForNumericType(Src value) { 21 constexpr bool IsValueInRangeForNumericType(Src value) {
54 return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid(); 22 return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid();
55 } 23 }
56 24
57 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. 25 // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
58 struct CheckOnFailure { 26 struct CheckOnFailure {
59 template <typename T> 27 template <typename T>
(...skipping 15 matching lines...) Expand all
75 typename Src> 43 typename Src>
76 constexpr Dst checked_cast(Src value) { 44 constexpr Dst checked_cast(Src value) {
77 // This throws a compile-time error on evaluating the constexpr if it can be 45 // This throws a compile-time error on evaluating the constexpr if it can be
78 // determined at compile-time as failing, otherwise it will CHECK at runtime. 46 // determined at compile-time as failing, otherwise it will CHECK at runtime.
79 using SrcType = typename internal::UnderlyingType<Src>::type; 47 using SrcType = typename internal::UnderlyingType<Src>::type;
80 return IsValueInRangeForNumericType<Dst, SrcType>(value) 48 return IsValueInRangeForNumericType<Dst, SrcType>(value)
81 ? static_cast<Dst>(static_cast<SrcType>(value)) 49 ? static_cast<Dst>(static_cast<SrcType>(value))
82 : CheckHandler::template HandleFailure<Dst>(); 50 : CheckHandler::template HandleFailure<Dst>();
83 } 51 }
84 52
53 // as_signed<> returns the supplied integral value (or integral castable
54 // Numeric template) cast as a signed integral of equivalent precision.
55 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
56 template <typename Src>
57 constexpr typename std::make_signed<
58 typename base::internal::UnderlyingType<Src>::type>::type
59 as_signed(const Src value) {
60 static_assert(std::is_integral<decltype(as_signed(value))>::value,
61 "Argument must be a signed or unsigned integer type.");
62 return static_cast<decltype(as_signed(value))>(value);
63 }
64
65 // as_unsigned<> returns the supplied integral value (or integral castable
66 // Numeric template) cast as an unsigned integral of equivalent precision.
67 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
68 template <typename Src>
69 constexpr typename std::make_unsigned<
70 typename base::internal::UnderlyingType<Src>::type>::type
71 as_unsigned(const Src value) {
72 static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
73 "Argument must be a signed or unsigned integer type.");
74 return static_cast<decltype(as_unsigned(value))>(value);
75 }
76
85 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. 77 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
86 template <typename T> 78 template <typename T>
87 struct SaturationDefaultHandler { 79 struct SaturationDefaultLimits : public std::numeric_limits<T> {
88 static constexpr T NaN() { 80 static constexpr T NaN() {
89 return std::numeric_limits<T>::has_quiet_NaN 81 return std::numeric_limits<T>::has_quiet_NaN
90 ? std::numeric_limits<T>::quiet_NaN() 82 ? std::numeric_limits<T>::quiet_NaN()
91 : T(); 83 : T();
92 } 84 }
93 static constexpr T max() { return std::numeric_limits<T>::max(); } 85 // static constexpr T required() is required by the library.
dcheng 2017/06/28 07:25:06 required() => max()? Also I don't quite understan
jschuh 2017/06/28 12:32:38 D'oh! Typo. But the gist is that you can provide a
94 static constexpr T Overflow() { 86 static constexpr T Overflow() {
95 return std::numeric_limits<T>::has_infinity 87 return std::numeric_limits<T>::has_infinity
96 ? std::numeric_limits<T>::infinity() 88 ? std::numeric_limits<T>::infinity()
97 : std::numeric_limits<T>::max(); 89 : std::numeric_limits<T>::max();
98 } 90 }
99 static constexpr T lowest() { return std::numeric_limits<T>::lowest(); } 91 // static constexpr T lowest() is required by the library.
100 static constexpr T Underflow() { 92 static constexpr T Underflow() {
101 return std::numeric_limits<T>::has_infinity 93 return std::numeric_limits<T>::has_infinity
102 ? std::numeric_limits<T>::infinity() * -1 94 ? std::numeric_limits<T>::infinity() * -1
103 : std::numeric_limits<T>::lowest(); 95 : std::numeric_limits<T>::lowest();
104 } 96 }
105 }; 97 };
106 98
107 namespace internal { 99 namespace internal {
108 100
109 template <typename Dst, template <typename> class S, typename Src> 101 template <typename Dst, template <typename> class S, typename Src>
110 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { 102 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
111 // For some reason clang generates much better code when the branch is 103 // For some reason clang generates much better code when the branch is
112 // structured exactly this way, rather than a sequence of checks. 104 // structured exactly this way, rather than a sequence of checks.
113 return !constraint.IsOverflowFlagSet() 105 return !constraint.IsOverflowFlagSet()
114 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) 106 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
115 : S<Dst>::Underflow()) 107 : S<Dst>::Underflow())
116 // Skip this check for integral Src, which cannot be NaN. 108 // Skip this check for integral Src, which cannot be NaN.
117 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() 109 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
118 ? S<Dst>::Overflow() 110 ? S<Dst>::Overflow()
119 : S<Dst>::NaN()); 111 : S<Dst>::NaN());
120 } 112 }
121 113
122 // saturated_cast<> is analogous to static_cast<> for numeric types, except 114 // saturated_cast<> is analogous to static_cast<> for numeric types, except
123 // that the specified numeric conversion will saturate by default rather than 115 // that the specified numeric conversion will saturate by default rather than
124 // overflow or underflow, and NaN assignment to an integral will return 0. 116 // overflow or underflow, and NaN assignment to an integral will return 0.
125 // All boundary condition behaviors can be overriden with a custom handler. 117 // All boundary condition behaviors can be overriden with a custom handler.
126 template <typename Dst, 118 template <typename Dst,
127 template <typename> 119 template <typename> class SaturationHandler = SaturationDefaultLimits,
128 class SaturationHandler = SaturationDefaultHandler,
129 typename Src> 120 typename Src>
130 constexpr Dst saturated_cast(Src value) { 121 constexpr Dst saturated_cast(Src value) {
131 using SrcType = typename UnderlyingType<Src>::type; 122 using SrcType = typename UnderlyingType<Src>::type;
132 return saturated_cast_impl<Dst, SaturationHandler, SrcType>( 123 return saturated_cast_impl<Dst, SaturationHandler, SrcType>(
133 value, 124 value,
134 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value)); 125 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value));
135 } 126 }
136 127
137 // strict_cast<> is analogous to static_cast<> for numeric types, except that 128 // strict_cast<> is analogous to static_cast<> for numeric types, except that
138 // it will cause a compile failure if the destination type is not large enough 129 // it will cause a compile failure if the destination type is not large enough
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 return value; 222 return value;
232 } 223 }
233 224
234 // Overload the ostream output operator to make logging work nicely. 225 // Overload the ostream output operator to make logging work nicely.
235 template <typename T> 226 template <typename T>
236 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { 227 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
237 os << static_cast<T>(value); 228 os << static_cast<T>(value);
238 return os; 229 return os;
239 } 230 }
240 231
241 #define STRICT_COMPARISON_OP(NAME, OP) \ 232 #define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
jschuh 2017/06/26 20:26:50 Generalized the implementation so ClampedNumeric c
242 template <typename L, typename R, \ 233 template <typename L, typename R, \
243 typename std::enable_if< \ 234 typename std::enable_if< \
244 internal::IsStrictOp<L, R>::value>::type* = nullptr> \ 235 internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
245 constexpr bool operator OP(const L lhs, const R rhs) { \ 236 constexpr bool operator OP(const L lhs, const R rhs) { \
246 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ 237 return SafeCompare<NAME, typename UnderlyingType<L>::type, \
247 typename UnderlyingType<R>::type>(lhs, rhs); \ 238 typename UnderlyingType<R>::type>(lhs, rhs); \
248 } 239 }
249 240
250 STRICT_COMPARISON_OP(IsLess, <); 241 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <);
251 STRICT_COMPARISON_OP(IsLessOrEqual, <=); 242 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=);
252 STRICT_COMPARISON_OP(IsGreater, >); 243 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >);
253 STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); 244 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=);
254 STRICT_COMPARISON_OP(IsEqual, ==); 245 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==);
255 STRICT_COMPARISON_OP(IsNotEqual, !=); 246 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=);
256 247
257 #undef STRICT_COMPARISON_OP 248 }; // namespace internal
258 };
259 249
260 using internal::strict_cast; 250 using internal::strict_cast;
261 using internal::saturated_cast; 251 using internal::saturated_cast;
262 using internal::SafeUnsignedAbs; 252 using internal::SafeUnsignedAbs;
263 using internal::StrictNumeric; 253 using internal::StrictNumeric;
264 using internal::MakeStrictNum; 254 using internal::MakeStrictNum;
265 using internal::IsValueNegative; 255 using internal::IsValueNegative;
266 256
267 // Explicitly make a shorter size_t alias for convenience. 257 // Explicitly make a shorter size_t alias for convenience.
268 using SizeT = StrictNumeric<size_t>; 258 using SizeT = StrictNumeric<size_t>;
269 259
270 } // namespace base 260 } // namespace base
271 261
272 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ 262 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698