|
OLD | NEW |
---|---|
(Empty) | |
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_SAFE_NUMERICS_IMPL_H_ | |
6 #define BASE_SAFE_NUMERICS_IMPL_H_ | |
7 | |
8 #include <limits> | |
9 | |
10 #include "base/macros.h" | |
11 | |
12 namespace base { | |
13 namespace internal { | |
14 | |
15 enum DstSign { | |
16 DST_UNSIGNED, | |
17 DST_SIGNED | |
18 }; | |
19 | |
20 enum SrcSign { | |
21 SRC_UNSIGNED, | |
22 SRC_SIGNED | |
23 }; | |
24 | |
25 enum DstRange { | |
26 OVERLAPS_RANGE, | |
27 CONTAINS_RANGE | |
28 }; | |
29 | |
30 // Helper templates to statically determine if our destination type can contain | |
31 // all values represented by the source type. | |
32 | |
33 template <typename Dst, typename Src, | |
34 DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ? | |
35 DST_SIGNED : DST_UNSIGNED, | |
36 SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ? | |
37 SRC_SIGNED : SRC_UNSIGNED> | |
38 struct StaticRangeCheck {}; | |
39 | |
40 template <typename Dst, typename Src> | |
41 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> { | |
42 // Pad floating point value sizes so they're treated as larger than integral. | |
43 static const size_t DstEffectiveSize = sizeof(Dst) << | |
akalin
2014/01/16 00:21:55
I'm a bit confused by this padding. From your comm
jschuh
2014/01/16 01:26:31
Good point. I'll use max_exponent and add a clarif
| |
44 (std::numeric_limits<Dst>::is_iec559 * 2); | |
45 static const size_t SrcEffectiveSize = sizeof(Src) << | |
46 (std::numeric_limits<Src>::is_iec559 * 2); | |
47 static const DstRange value = DstEffectiveSize >= SrcEffectiveSize ? | |
48 CONTAINS_RANGE : OVERLAPS_RANGE; | |
49 }; | |
50 | |
51 template <typename Dst, typename Src> | |
52 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> { | |
53 static const DstRange value = sizeof(Dst) >= sizeof(Src) ? | |
54 CONTAINS_RANGE : OVERLAPS_RANGE; | |
55 }; | |
56 | |
57 template <typename Dst, typename Src> | |
58 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> { | |
59 // Pad floating point value sizes so they're treated as larger than integral. | |
60 static const size_t DstEffectiveSize = sizeof(Dst) << | |
61 (std::numeric_limits<Dst>::is_iec559 * 2); | |
62 static const size_t SrcEffectiveSize = sizeof(Src) << | |
akalin
2014/01/16 00:21:55
src is unsigned, so it can't be iec559, right? (I
jschuh
2014/01/16 01:26:31
Done.
| |
63 (std::numeric_limits<Src>::is_iec559 * 2); | |
64 static const DstRange value = DstEffectiveSize > SrcEffectiveSize ? | |
65 CONTAINS_RANGE : OVERLAPS_RANGE; | |
66 }; | |
67 | |
68 template <typename Dst, typename Src> | |
69 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> { | |
70 static const DstRange value = OVERLAPS_RANGE; | |
71 }; | |
72 | |
73 | |
74 enum RangeCheckResult { | |
75 TYPE_VALID = 0, // Value can be represented by the destination type. | |
76 TYPE_UNDERFLOW = 1, // Value would overflow. | |
77 TYPE_OVERFLOW = 2, // Value would underflow. | |
78 TYPE_INVALID = 3 // Source value is invalid (i.e. NaN). | |
79 }; | |
80 | |
81 // This macro creates a RangeCheckResult from an upper and lower bound | |
82 // check by taking advantage of the fact that only NaN can be out of range in | |
83 // both directions at once. | |
84 #define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ | |
85 RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ | |
86 ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) | |
87 | |
88 template <typename Dst, | |
89 typename Src, | |
90 DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ? | |
91 DST_SIGNED : DST_UNSIGNED, | |
92 SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ? | |
93 SRC_SIGNED : SRC_UNSIGNED, | |
94 DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value> | |
95 struct RangeCheckImpl {}; | |
96 | |
97 // The following templates are for ranges that must be verified at runtime. We | |
98 // split it into checks based on signedness to avoid confusing casts and | |
99 // compiler warnings on signed an unsigned comparisons. | |
100 | |
101 // Dst range always contains the result: nothing to check. | |
102 template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned> | |
103 struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, | |
104 CONTAINS_RANGE> { | |
akalin
2014/01/16 00:21:55
append to prev line?
jschuh
2014/01/16 01:26:31
Done.
| |
105 static RangeCheckResult Check(Src value) { | |
106 return BASE_NUMERIC_RANGE_CHECK_RESULT(true, true); | |
akalin
2014/01/16 00:21:55
probably clearer to return TYPE_VALID directly
jschuh
2014/01/16 01:26:31
Done.
| |
107 } | |
108 }; | |
109 | |
110 // Signed to signed narrowing. | |
111 template <typename Dst, typename Src> | |
112 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> { | |
113 static RangeCheckResult Check(Src value) { | |
114 typedef std::numeric_limits<Dst> DstLimits; | |
115 return DstLimits::is_iec559 ? | |
116 BASE_NUMERIC_RANGE_CHECK_RESULT( | |
117 value <= static_cast<Src>(DstLimits::max()), | |
118 value >= static_cast<Src>(DstLimits::max() * -1)) : | |
119 BASE_NUMERIC_RANGE_CHECK_RESULT( | |
120 value <= static_cast<Src>(DstLimits::max()), | |
121 value >= static_cast<Src>(DstLimits::min())); | |
122 } | |
123 }; | |
124 | |
125 // Unsigned to unsigned narrowing. | |
126 template <typename Dst, typename Src> | |
127 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { | |
128 static RangeCheckResult Check(Src value) { | |
129 typedef std::numeric_limits<Dst> DstLimits; | |
130 return BASE_NUMERIC_RANGE_CHECK_RESULT( | |
131 value <= static_cast<Src>(DstLimits::max()), true); | |
132 } | |
133 }; | |
134 | |
135 // Unsigned to signed. | |
136 template <typename Dst, typename Src> | |
137 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { | |
138 static RangeCheckResult Check(Src value) { | |
139 typedef std::numeric_limits<Dst> DstLimits; | |
140 return sizeof(Dst) > sizeof(Src) ? | |
141 BASE_NUMERIC_RANGE_CHECK_RESULT(true, true) : | |
akalin
2014/01/16 00:21:55
TYPE_VALID?
jschuh
2014/01/16 01:26:31
Done.
| |
142 BASE_NUMERIC_RANGE_CHECK_RESULT( | |
143 value <= static_cast<Src>(DstLimits::max()), true); | |
144 } | |
145 }; | |
146 | |
147 // Signed to unsigned. | |
148 template <typename Dst, typename Src> | |
149 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> { | |
150 static RangeCheckResult Check(Src value) { | |
151 typedef std::numeric_limits<Src> SrcLimits; | |
152 typedef std::numeric_limits<Dst> DstLimits; | |
153 return (SrcLimits::is_integer && sizeof(Dst) >= sizeof(Src)) ? | |
akalin
2014/01/16 00:21:55
you can probably relax this check if Dst is a floa
jschuh
2014/01/16 01:26:31
But Dst is unsigned here, so it can't be a float.
| |
154 BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast<Src>(0)) : | |
155 BASE_NUMERIC_RANGE_CHECK_RESULT( | |
156 value <= static_cast<Src>(DstLimits::max()), | |
157 value >= static_cast<Src>(0)); | |
158 } | |
159 }; | |
160 | |
161 template <typename Dst, typename Src> | |
162 inline RangeCheckResult RangeCheck(Src value) { | |
163 COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, | |
164 argument_must_be_numeric); | |
165 COMPILE_ASSERT(std::numeric_limits<Dst>::is_specialized, | |
166 result_must_be_numeric); | |
167 return RangeCheckImpl<Dst, Src>::Check(value); | |
168 } | |
169 | |
170 } // namespace internal | |
171 } // namespace base | |
172 | |
173 #endif // BASE_SAFE_NUMERICS_IMPL_H_ | |
174 | |
OLD | NEW |