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

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

Issue 141583008: Add support for safe math operations in base/numerics (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: style fixes Created 6 years, 10 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 | Annotate | Revision Log
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_SAFE_CONVERSIONS_IMPL_H_ 5 #ifndef BASE_SAFE_CONVERSIONS_IMPL_H_
6 #define BASE_SAFE_CONVERSIONS_IMPL_H_ 6 #define BASE_SAFE_CONVERSIONS_IMPL_H_
7 7
8 #include <limits> 8 #include <limits>
9 9
10 #include "base/macros.h" 10 #include "base/macros.h"
11 #include "base/template_util.h"
11 12
12 namespace base { 13 namespace base {
13 namespace internal { 14 namespace internal {
14 15
15 enum DstSign { 16 using std::numeric_limits;
16 DST_UNSIGNED,
17 DST_SIGNED
18 };
19 17
20 enum SrcSign { 18 enum DstSignId { DST_UNSIGNED = 0, DST_SIGNED = 1 };
awong 2014/02/11 19:40:10 (push back on this if you disagree) DstSignId and
jschuh 2014/02/21 19:22:28 Done. Fred actually wanted these split, because he
21 SRC_UNSIGNED,
22 SRC_SIGNED
23 };
24 19
25 enum DstRange { 20 enum SrcSignId { SRC_UNSIGNED = 0, SRC_SIGNED = 1 };
26 OVERLAPS_RANGE, 21
27 CONTAINS_RANGE 22 enum DstRangeId { OVERLAPS_RANGE = 0, CONTAINS_RANGE = 1 };
awong 2014/02/11 19:40:10 nit: enum DstRange { DST_RANGE_OVERLAPS, DST_
jschuh 2014/02/21 19:22:28 Done.
23
24 // Retrieve the max exponent for floating types and compute it for integrals.
awong 2014/02/11 19:40:10 In the comment, prefer explaining why the math is
jschuh 2014/02/21 19:22:28 Done.
25 template <typename NumericType>
awong 2014/02/11 19:40:10 s/NumericType/IntegralType/ Also, is there an eas
jschuh 2014/02/21 19:22:28 NumericType is correct because it handles both flo
26 struct MaxExponent {
27 static const int value = numeric_limits<NumericType>::is_iec559
28 ? numeric_limits<NumericType>::max_exponent
29 : (sizeof(NumericType) * 8 + 1 -
30 numeric_limits<NumericType>::is_signed);
28 }; 31 };
29 32
30 // Helper templates to statically determine if our destination type can contain 33 // Helper templates to statically determine if our destination type can contain
31 // all values represented by the source type. 34 // maximum and minimum values represented by the source type.
32 35
33 template <typename Dst, typename Src, 36 template <typename Dst,
34 DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ? 37 typename Src,
35 DST_SIGNED : DST_UNSIGNED, 38 DstSignId DstSign = numeric_limits<Dst>::is_signed ? DST_SIGNED
36 SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ? 39 : DST_UNSIGNED,
37 SRC_SIGNED : SRC_UNSIGNED> 40 SrcSignId SrcSign = numeric_limits<Src>::is_signed
38 struct StaticRangeCheck {}; 41 ? SRC_SIGNED
42 : SRC_UNSIGNED > struct StaticRangeCheck {};
awong 2014/02/11 19:40:10 Put the "struct" on a new line. As is, I spent 10
jschuh 2014/02/21 19:22:28 Done (but you can blame "git cl format" for that o
39 43
44 // Both signed, narrowing.
40 template <typename Dst, typename Src> 45 template <typename Dst, typename Src>
41 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> { 46 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> {
42 typedef std::numeric_limits<Dst> DstLimits; 47 static const DstRangeId value =
43 typedef std::numeric_limits<Src> SrcLimits; 48 MaxExponent<Dst>::value >= MaxExponent<Src>::value ? CONTAINS_RANGE
44 // Compare based on max_exponent, which we must compute for integrals. 49 : OVERLAPS_RANGE;
45 static const size_t kDstMaxExponent = DstLimits::is_iec559 ?
46 DstLimits::max_exponent :
47 (sizeof(Dst) * 8 - 1);
48 static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ?
49 SrcLimits::max_exponent :
50 (sizeof(Src) * 8 - 1);
51 static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ?
52 CONTAINS_RANGE : OVERLAPS_RANGE;
53 }; 50 };
54 51
52 // Both unsigned, narrowing (handled same as both signed narrowing).
55 template <typename Dst, typename Src> 53 template <typename Dst, typename Src>
56 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> { 54 struct StaticRangeCheck<
57 static const DstRange value = sizeof(Dst) >= sizeof(Src) ? 55 Dst,
58 CONTAINS_RANGE : OVERLAPS_RANGE; 56 Src,
57 DST_UNSIGNED,
58 SRC_UNSIGNED> : StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> {};
59
60 // Unsigned to signed, overlapping.
awong 2014/02/11 19:40:10 These comments in general don't explain why the re
jschuh 2014/02/21 19:22:28 Done.
61 template <typename Dst, typename Src>
62 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> {
awong 2014/02/11 19:40:10 Maybe called this DstRangeRelationToSrcRange or s
jschuh 2014/02/21 19:22:28 Done.
63 static const DstRangeId value =
64 MaxExponent<Dst>::value > MaxExponent<Src>::value ? CONTAINS_RANGE
awong 2014/02/11 19:40:10 >= doesn't constitute contains?
jschuh 2014/02/21 19:22:28 Done.
65 : OVERLAPS_RANGE;
59 }; 66 };
60 67
68 // Signed to unsigned, overlapping.
61 template <typename Dst, typename Src> 69 template <typename Dst, typename Src>
62 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> { 70 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> {
63 typedef std::numeric_limits<Dst> DstLimits; 71 static const DstRangeId value = OVERLAPS_RANGE;
64 typedef std::numeric_limits<Src> SrcLimits;
65 // Compare based on max_exponent, which we must compute for integrals.
66 static const size_t kDstMaxExponent = DstLimits::is_iec559 ?
67 DstLimits::max_exponent :
68 (sizeof(Dst) * 8 - 1);
69 static const size_t kSrcMaxExponent = sizeof(Src) * 8;
70 static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ?
71 CONTAINS_RANGE : OVERLAPS_RANGE;
72 }; 72 };
73 73
74 template <typename Dst, typename Src> 74 enum RangeCheckId {
awong 2014/02/11 19:40:10 nit: "Id" is a weird suffix for enums here. These
jschuh 2014/02/21 19:22:28 Done.
75 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> {
76 static const DstRange value = OVERLAPS_RANGE;
77 };
78
79
80 enum RangeCheckResult {
81 TYPE_VALID = 0, // Value can be represented by the destination type. 75 TYPE_VALID = 0, // Value can be represented by the destination type.
82 TYPE_UNDERFLOW = 1, // Value would overflow. 76 TYPE_UNDERFLOW = 1, // Value would overflow.
83 TYPE_OVERFLOW = 2, // Value would underflow. 77 TYPE_OVERFLOW = 2, // Value would underflow.
84 TYPE_INVALID = 3 // Source value is invalid (i.e. NaN). 78 TYPE_INVALID = 3 // Source value is invalid (i.e. NaN).
85 }; 79 };
86 80
87 // This macro creates a RangeCheckResult from an upper and lower bound 81 // This macro creates a RangeCheckId from an upper and lower bound
88 // check by taking advantage of the fact that only NaN can be out of range in 82 // check by taking advantage of the fact that only NaN can be out of range in
89 // both directions at once. 83 // both directions at once.
90 #define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ 84 #define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \
awong 2014/02/11 19:40:10 Why is this a macro instead of a static inline fun
jschuh 2014/02/21 19:22:28 Done. This needed to be a macro several versions b
91 RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ 85 RangeCheckId(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \
92 ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) 86 ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW))
93 87
94 template <typename Dst, 88 template <typename Dst,
95 typename Src, 89 typename Src,
96 DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ? 90 DstSignId DstSign = numeric_limits<Dst>::is_signed ? DST_SIGNED
97 DST_SIGNED : DST_UNSIGNED, 91 : DST_UNSIGNED,
98 SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ? 92 SrcSignId SrcSign =
99 SRC_SIGNED : SRC_UNSIGNED, 93 numeric_limits<Src>::is_signed ? SRC_SIGNED : SRC_UNSIGNED,
100 DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value> 94 DstRangeId DstRange =
101 struct RangeCheckImpl {}; 95 StaticRangeCheck<Dst, Src>::value > struct RangeCheckImpl {};
awong 2014/02/11 19:40:10 Don't define this struct. Only declare it. That w
jschuh 2014/02/21 19:22:28 Done.
102 96
103 // The following templates are for ranges that must be verified at runtime. We 97 // The following templates are for ranges that must be verified at runtime. We
104 // split it into checks based on signedness to avoid confusing casts and 98 // split it into checks based on signedness to avoid confusing casts and
105 // compiler warnings on signed an unsigned comparisons. 99 // compiler warnings on signed an unsigned comparisons.
106 100
107 // Dst range always contains the result: nothing to check. 101 // Dst range always contains the result: nothing to check.
108 template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned> 102 template <typename Dst, typename Src, DstSignId DstSign, SrcSignId SrcSign>
109 struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> { 103 struct RangeCheckImpl<Dst, Src, DstSign, SrcSign, CONTAINS_RANGE> {
110 static RangeCheckResult Check(Src value) { 104 static RangeCheckId Check(Src value) { return TYPE_VALID; }
111 return TYPE_VALID;
112 }
113 }; 105 };
114 106
115 // Signed to signed narrowing. 107 // Signed to signed narrowing.
116 template <typename Dst, typename Src> 108 template <typename Dst, typename Src>
117 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> { 109 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
118 static RangeCheckResult Check(Src value) { 110 static RangeCheckId Check(Src value) {
119 typedef std::numeric_limits<Dst> DstLimits; 111 return numeric_limits<Dst>::is_iec559
120 return DstLimits::is_iec559 ? 112 ? BASE_NUMERIC_RANGE_CHECK_RESULT(
121 BASE_NUMERIC_RANGE_CHECK_RESULT( 113 value <= numeric_limits<Dst>::max(),
122 value <= static_cast<Src>(DstLimits::max()), 114 value >= -numeric_limits<Dst>::max())
123 value >= static_cast<Src>(DstLimits::max() * -1)) : 115 : BASE_NUMERIC_RANGE_CHECK_RESULT(
124 BASE_NUMERIC_RANGE_CHECK_RESULT( 116 value <= numeric_limits<Dst>::max(),
125 value <= static_cast<Src>(DstLimits::max()), 117 value >= numeric_limits<Dst>::min());
126 value >= static_cast<Src>(DstLimits::min()));
127 } 118 }
128 }; 119 };
129 120
130 // Unsigned to unsigned narrowing. 121 // Unsigned to unsigned narrowing.
131 template <typename Dst, typename Src> 122 template <typename Dst, typename Src>
132 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { 123 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
133 static RangeCheckResult Check(Src value) { 124 static RangeCheckId Check(Src value) {
134 typedef std::numeric_limits<Dst> DstLimits; 125 return BASE_NUMERIC_RANGE_CHECK_RESULT(value <= numeric_limits<Dst>::max(),
135 return BASE_NUMERIC_RANGE_CHECK_RESULT( 126 true);
136 value <= static_cast<Src>(DstLimits::max()), true);
137 } 127 }
138 }; 128 };
139 129
140 // Unsigned to signed. 130 // Unsigned to signed.
141 template <typename Dst, typename Src> 131 template <typename Dst, typename Src>
142 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { 132 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
143 static RangeCheckResult Check(Src value) { 133 static RangeCheckId Check(Src value) {
144 typedef std::numeric_limits<Dst> DstLimits; 134 return sizeof(Dst) > sizeof(Src)
145 return sizeof(Dst) > sizeof(Src) ? TYPE_VALID : 135 ? TYPE_VALID
146 BASE_NUMERIC_RANGE_CHECK_RESULT( 136 : BASE_NUMERIC_RANGE_CHECK_RESULT(
147 value <= static_cast<Src>(DstLimits::max()), true); 137 value <= static_cast<Src>(numeric_limits<Dst>::max()),
138 true);
148 } 139 }
149 }; 140 };
150 141
151 // Signed to unsigned. 142 // Signed to unsigned.
152 template <typename Dst, typename Src> 143 template <typename Dst, typename Src>
153 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> { 144 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
154 static RangeCheckResult Check(Src value) { 145 static RangeCheckId Check(Src value) {
155 typedef std::numeric_limits<Dst> DstLimits; 146 return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
156 typedef std::numeric_limits<Src> SrcLimits; 147 ? BASE_NUMERIC_RANGE_CHECK_RESULT(true,
157 // Compare based on max_exponent, which we must compute for integrals. 148 value >= static_cast<Src>(0))
158 static const size_t kDstMaxExponent = sizeof(Dst) * 8; 149 : BASE_NUMERIC_RANGE_CHECK_RESULT(
159 static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ? 150 value <= static_cast<Src>(numeric_limits<Dst>::max()),
160 SrcLimits::max_exponent : 151 value >= static_cast<Src>(0));
161 (sizeof(Src) * 8 - 1);
162 return (kDstMaxExponent >= kSrcMaxExponent) ?
163 BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast<Src>(0)) :
164 BASE_NUMERIC_RANGE_CHECK_RESULT(
165 value <= static_cast<Src>(DstLimits::max()),
166 value >= static_cast<Src>(0));
167 } 152 }
168 }; 153 };
169 154
170 template <typename Dst, typename Src> 155 template <typename Dst, typename Src>
171 inline RangeCheckResult RangeCheck(Src value) { 156 inline RangeCheckId RangeCheck(Src value) {
172 COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, 157 COMPILE_ASSERT(numeric_limits<Src>::is_specialized, argument_must_be_numeric);
173 argument_must_be_numeric); 158 COMPILE_ASSERT(numeric_limits<Dst>::is_specialized, result_must_be_numeric);
174 COMPILE_ASSERT(std::numeric_limits<Dst>::is_specialized,
175 result_must_be_numeric);
176 return RangeCheckImpl<Dst, Src>::Check(value); 159 return RangeCheckImpl<Dst, Src>::Check(value);
177 } 160 }
178 161
179 } // namespace internal 162 } // namespace internal
180 } // namespace base 163 } // namespace base
181 164
182 #endif // BASE_SAFE_CONVERSIONS_IMPL_H_ 165 #endif // BASE_SAFE_CONVERSIONS_IMPL_H_
183 166
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/numerics/safe_math.h » ('j') | base/numerics/safe_math.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698