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_CONVERSIONS_IMPL_H_ | 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
7 | 7 |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <limits> | 10 #include <limits> |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
159 | 159 |
160 // Signed to unsigned: Dst cannot be statically determined to contain Src. | 160 // Signed to unsigned: Dst cannot be statically determined to contain Src. |
161 template <typename Dst, typename Src> | 161 template <typename Dst, typename Src> |
162 struct StaticDstRangeRelationToSrcRange<Dst, | 162 struct StaticDstRangeRelationToSrcRange<Dst, |
163 Src, | 163 Src, |
164 INTEGER_REPRESENTATION_UNSIGNED, | 164 INTEGER_REPRESENTATION_UNSIGNED, |
165 INTEGER_REPRESENTATION_SIGNED> { | 165 INTEGER_REPRESENTATION_SIGNED> { |
166 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; | 166 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; |
167 }; | 167 }; |
168 | 168 |
169 enum RangeConstraint { | 169 enum RangeConstraintEnum { |
170 RANGE_VALID = 0x0, // Value can be represented by the destination type. | 170 RANGE_VALID = 0x0, // Value can be represented by the destination type. |
171 RANGE_UNDERFLOW = 0x1, // Value would overflow. | 171 RANGE_OVERFLOW = 0x1, // Value would overflow. |
172 RANGE_OVERFLOW = 0x2, // Value would underflow. | 172 RANGE_UNDERFLOW = 0x2, // Value would underflow. |
173 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). | 173 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). |
174 }; | 174 }; |
175 | 175 |
176 // Helper function for coercing an int back to a RangeContraint. | 176 // This class wraps the range constraints as separate booleans so the compiler |
177 constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint) { | 177 // can identify constants and eliminate unused code paths. |
178 // TODO(jschuh): Once we get full C++14 support we want this | 178 class RangeConstraint { |
179 // assert(integer_range_constraint >= RANGE_VALID && | 179 public: |
180 // integer_range_constraint <= RANGE_INVALID) | 180 constexpr RangeConstraint(bool is_in_upper_bound, bool is_in_lower_bound) |
181 return static_cast<RangeConstraint>(integer_range_constraint); | 181 : is_overflow_(!is_in_upper_bound), is_underflow_(!is_in_lower_bound) {} |
182 } | 182 constexpr RangeConstraint() : is_overflow_(0), is_underflow_(0) {} |
| 183 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } |
| 184 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } |
| 185 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } |
| 186 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } |
183 | 187 |
184 // This function creates a RangeConstraint from an upper and lower bound | 188 // These are some wrappers to make the tests a bit cleaner. |
185 // check by taking advantage of the fact that only NaN can be out of range in | 189 constexpr operator RangeConstraintEnum() const { |
186 // both directions at once. | 190 return static_cast<RangeConstraintEnum>( |
187 constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, | 191 static_cast<int>(is_overflow_) | static_cast<int>(is_underflow_) << 1); |
188 bool is_in_lower_bound) { | 192 } |
189 return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | | 193 constexpr bool operator==(const RangeConstraintEnum rhs) const { |
190 (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); | 194 return rhs == static_cast<RangeConstraintEnum>(*this); |
191 } | 195 } |
| 196 |
| 197 private: |
| 198 const bool is_overflow_; |
| 199 const bool is_underflow_; |
| 200 }; |
192 | 201 |
193 // The following helper template addresses a corner case in range checks for | 202 // The following helper template addresses a corner case in range checks for |
194 // conversion from a floating-point type to an integral type of smaller range | 203 // conversion from a floating-point type to an integral type of smaller range |
195 // but larger precision (e.g. float -> unsigned). The problem is as follows: | 204 // but larger precision (e.g. float -> unsigned). The problem is as follows: |
196 // 1. Integral maximum is always one less than a power of two, so it must be | 205 // 1. Integral maximum is always one less than a power of two, so it must be |
197 // truncated to fit the mantissa of the floating point. The direction of | 206 // truncated to fit the mantissa of the floating point. The direction of |
198 // rounding is implementation defined, but by default it's always IEEE | 207 // rounding is implementation defined, but by default it's always IEEE |
199 // floats, which round to nearest and thus result in a value of larger | 208 // floats, which round to nearest and thus result in a value of larger |
200 // magnitude than the integral value. | 209 // magnitude than the integral value. |
201 // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX | 210 // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX |
202 // // is 4294967295u. | 211 // // is 4294967295u. |
203 // 2. If the floating point value is equal to the promoted integral maximum | 212 // 2. If the floating point value is equal to the promoted integral maximum |
204 // value, a range check will erroneously pass. | 213 // value, a range check will erroneously pass. |
205 // Example: (4294967296f <= 4294967295u) // This is true due to a precision | 214 // Example: (4294967296f <= 4294967295u) // This is true due to a precision |
206 // // loss in rounding up to float. | 215 // // loss in rounding up to float. |
207 // 3. When the floating point value is then converted to an integral, the | 216 // 3. When the floating point value is then converted to an integral, the |
208 // resulting value is out of range for the target integral type and | 217 // resulting value is out of range for the target integral type and |
209 // thus is implementation defined. | 218 // thus is implementation defined. |
210 // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. | 219 // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. |
211 // To fix this bug we manually truncate the maximum value when the destination | 220 // To fix this bug we manually truncate the maximum value when the destination |
212 // type is an integral of larger precision than the source floating-point type, | 221 // type is an integral of larger precision than the source floating-point type, |
213 // such that the resulting maximum is represented exactly as a floating point. | 222 // such that the resulting maximum is represented exactly as a floating point. |
214 template <typename Dst, | 223 template <typename Dst, typename Src, template <typename> class Bounds> |
215 typename Src, | |
216 template <typename> class Bounds = std::numeric_limits> | |
217 struct NarrowingRange { | 224 struct NarrowingRange { |
218 using SrcLimits = typename std::numeric_limits<Src>; | 225 using SrcLimits = std::numeric_limits<Src>; |
219 using DstLimits = typename std::numeric_limits<Dst>; | 226 using DstLimits = typename std::numeric_limits<Dst>; |
220 | 227 |
221 // Computes the mask required to make an accurate comparison between types. | 228 // Computes the mask required to make an accurate comparison between types. |
222 static const int kShift = | 229 static const int kShift = |
223 (MaxExponent<Src>::value > MaxExponent<Dst>::value && | 230 (MaxExponent<Src>::value > MaxExponent<Dst>::value && |
224 SrcLimits::digits < DstLimits::digits) | 231 SrcLimits::digits < DstLimits::digits) |
225 ? (DstLimits::digits - SrcLimits::digits) | 232 ? (DstLimits::digits - SrcLimits::digits) |
226 : 0; | 233 : 0; |
227 template < | 234 template < |
228 typename T, | 235 typename T, |
(...skipping 17 matching lines...) Expand all Loading... |
246 static_assert(kShift == 0, ""); | 253 static_assert(kShift == 0, ""); |
247 return value; | 254 return value; |
248 } | 255 } |
249 | 256 |
250 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } | 257 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } |
251 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } | 258 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } |
252 }; | 259 }; |
253 | 260 |
254 template <typename Dst, | 261 template <typename Dst, |
255 typename Src, | 262 typename Src, |
| 263 template <typename> class Bounds, |
256 IntegerRepresentation DstSign = std::is_signed<Dst>::value | 264 IntegerRepresentation DstSign = std::is_signed<Dst>::value |
257 ? INTEGER_REPRESENTATION_SIGNED | 265 ? INTEGER_REPRESENTATION_SIGNED |
258 : INTEGER_REPRESENTATION_UNSIGNED, | 266 : INTEGER_REPRESENTATION_UNSIGNED, |
259 IntegerRepresentation SrcSign = std::is_signed<Src>::value | 267 IntegerRepresentation SrcSign = std::is_signed<Src>::value |
260 ? INTEGER_REPRESENTATION_SIGNED | 268 ? INTEGER_REPRESENTATION_SIGNED |
261 : INTEGER_REPRESENTATION_UNSIGNED, | 269 : INTEGER_REPRESENTATION_UNSIGNED, |
262 NumericRangeRepresentation DstRange = | 270 NumericRangeRepresentation DstRange = |
263 StaticDstRangeRelationToSrcRange<Dst, Src>::value> | 271 StaticDstRangeRelationToSrcRange<Dst, Src>::value> |
264 struct DstRangeRelationToSrcRangeImpl; | 272 struct DstRangeRelationToSrcRangeImpl; |
265 | 273 |
266 // The following templates are for ranges that must be verified at runtime. We | 274 // The following templates are for ranges that must be verified at runtime. We |
267 // split it into checks based on signedness to avoid confusing casts and | 275 // split it into checks based on signedness to avoid confusing casts and |
268 // compiler warnings on signed an unsigned comparisons. | 276 // compiler warnings on signed an unsigned comparisons. |
269 | 277 |
270 // Dst range is statically determined to contain Src: Nothing to check. | 278 // Same sign narrowing: The range is contained for normal limits. |
271 template <typename Dst, | 279 template <typename Dst, |
272 typename Src, | 280 typename Src, |
| 281 template <typename> class Bounds, |
273 IntegerRepresentation DstSign, | 282 IntegerRepresentation DstSign, |
274 IntegerRepresentation SrcSign> | 283 IntegerRepresentation SrcSign> |
275 struct DstRangeRelationToSrcRangeImpl<Dst, | 284 struct DstRangeRelationToSrcRangeImpl<Dst, |
276 Src, | 285 Src, |
| 286 Bounds, |
277 DstSign, | 287 DstSign, |
278 SrcSign, | 288 SrcSign, |
279 NUMERIC_RANGE_CONTAINED> { | 289 NUMERIC_RANGE_CONTAINED> { |
280 static constexpr RangeConstraint Check(Src value) { return RANGE_VALID; } | 290 static constexpr RangeConstraint Check(Src value) { |
| 291 using SrcLimits = std::numeric_limits<Src>; |
| 292 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 293 return RangeConstraint( |
| 294 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || |
| 295 static_cast<Dst>(value) <= DstLimits::max(), |
| 296 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || |
| 297 static_cast<Dst>(value) >= DstLimits::lowest()); |
| 298 } |
281 }; | 299 }; |
282 | 300 |
283 // Signed to signed narrowing: Both the upper and lower boundaries may be | 301 // Signed to signed narrowing: Both the upper and lower boundaries may be |
284 // exceeded. | 302 // exceeded for standard limits. |
285 template <typename Dst, typename Src> | 303 template <typename Dst, typename Src, template <typename> class Bounds> |
286 struct DstRangeRelationToSrcRangeImpl<Dst, | 304 struct DstRangeRelationToSrcRangeImpl<Dst, |
287 Src, | 305 Src, |
| 306 Bounds, |
288 INTEGER_REPRESENTATION_SIGNED, | 307 INTEGER_REPRESENTATION_SIGNED, |
289 INTEGER_REPRESENTATION_SIGNED, | 308 INTEGER_REPRESENTATION_SIGNED, |
290 NUMERIC_RANGE_NOT_CONTAINED> { | 309 NUMERIC_RANGE_NOT_CONTAINED> { |
291 static constexpr RangeConstraint Check(Src value) { | 310 static constexpr RangeConstraint Check(Src value) { |
292 return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()), | 311 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
293 (value >= NarrowingRange<Dst, Src>::lowest())); | 312 return RangeConstraint(value <= DstLimits::max(), |
| 313 value >= DstLimits::lowest()); |
294 } | 314 } |
295 }; | 315 }; |
296 | 316 |
297 // Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. | 317 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for |
298 template <typename Dst, typename Src> | 318 // standard limits. |
| 319 template <typename Dst, typename Src, template <typename> class Bounds> |
299 struct DstRangeRelationToSrcRangeImpl<Dst, | 320 struct DstRangeRelationToSrcRangeImpl<Dst, |
300 Src, | 321 Src, |
| 322 Bounds, |
301 INTEGER_REPRESENTATION_UNSIGNED, | 323 INTEGER_REPRESENTATION_UNSIGNED, |
302 INTEGER_REPRESENTATION_UNSIGNED, | 324 INTEGER_REPRESENTATION_UNSIGNED, |
303 NUMERIC_RANGE_NOT_CONTAINED> { | 325 NUMERIC_RANGE_NOT_CONTAINED> { |
304 static constexpr RangeConstraint Check(Src value) { | 326 static constexpr RangeConstraint Check(Src value) { |
305 return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true); | 327 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 328 return RangeConstraint( |
| 329 value <= DstLimits::max(), |
| 330 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest()); |
306 } | 331 } |
307 }; | 332 }; |
308 | 333 |
309 // Unsigned to signed: The upper boundary may be exceeded. | 334 // Unsigned to signed: Only the upper bound can be exceeded for standard limits. |
310 template <typename Dst, typename Src> | 335 template <typename Dst, typename Src, template <typename> class Bounds> |
311 struct DstRangeRelationToSrcRangeImpl<Dst, | 336 struct DstRangeRelationToSrcRangeImpl<Dst, |
312 Src, | 337 Src, |
| 338 Bounds, |
313 INTEGER_REPRESENTATION_SIGNED, | 339 INTEGER_REPRESENTATION_SIGNED, |
314 INTEGER_REPRESENTATION_UNSIGNED, | 340 INTEGER_REPRESENTATION_UNSIGNED, |
315 NUMERIC_RANGE_NOT_CONTAINED> { | 341 NUMERIC_RANGE_NOT_CONTAINED> { |
316 static constexpr RangeConstraint Check(Src value) { | 342 static constexpr RangeConstraint Check(Src value) { |
317 return IntegerBitsPlusSign<Dst>::value > IntegerBitsPlusSign<Src>::value | 343 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
318 ? RANGE_VALID | 344 using Promotion = decltype(Src() + Dst()); |
319 : GetRangeConstraint( | 345 return RangeConstraint(static_cast<Promotion>(value) <= |
320 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), | 346 static_cast<Promotion>(DstLimits::max()), |
321 true); | 347 DstLimits::lowest() <= Dst(0) || |
| 348 static_cast<Promotion>(value) >= |
| 349 static_cast<Promotion>(DstLimits::lowest())); |
322 } | 350 } |
323 }; | 351 }; |
324 | 352 |
325 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, | 353 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, |
326 // and any negative value exceeds the lower boundary. | 354 // and any negative value exceeds the lower boundary for standard limits. |
327 template <typename Dst, typename Src> | 355 template <typename Dst, typename Src, template <typename> class Bounds> |
328 struct DstRangeRelationToSrcRangeImpl<Dst, | 356 struct DstRangeRelationToSrcRangeImpl<Dst, |
329 Src, | 357 Src, |
| 358 Bounds, |
330 INTEGER_REPRESENTATION_UNSIGNED, | 359 INTEGER_REPRESENTATION_UNSIGNED, |
331 INTEGER_REPRESENTATION_SIGNED, | 360 INTEGER_REPRESENTATION_SIGNED, |
332 NUMERIC_RANGE_NOT_CONTAINED> { | 361 NUMERIC_RANGE_NOT_CONTAINED> { |
333 static constexpr RangeConstraint Check(Src value) { | 362 static constexpr RangeConstraint Check(Src value) { |
334 return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) | 363 using SrcLimits = std::numeric_limits<Src>; |
335 ? GetRangeConstraint(true, value >= static_cast<Src>(0)) | 364 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
336 : GetRangeConstraint( | 365 using Promotion = decltype(Src() + Dst()); |
337 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), | 366 return RangeConstraint( |
338 value >= static_cast<Src>(0)); | 367 static_cast<Promotion>(SrcLimits::max()) <= |
| 368 static_cast<Promotion>(DstLimits::max()) || |
| 369 static_cast<Promotion>(value) <= |
| 370 static_cast<Promotion>(DstLimits::max()), |
| 371 value >= Src(0) && (DstLimits::lowest() == 0 || |
| 372 static_cast<Dst>(value) >= DstLimits::lowest())); |
339 } | 373 } |
340 }; | 374 }; |
341 | 375 |
342 template <typename Dst, typename Src> | 376 template <typename Dst, |
| 377 template <typename> class Bounds = std::numeric_limits, |
| 378 typename Src> |
343 constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) { | 379 constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) { |
344 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); | 380 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); |
345 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 381 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); |
346 return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); | 382 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); |
| 383 return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); |
347 } | 384 } |
348 | 385 |
349 // Integer promotion templates used by the portable checked integer arithmetic. | 386 // Integer promotion templates used by the portable checked integer arithmetic. |
350 template <size_t Size, bool IsSigned> | 387 template <size_t Size, bool IsSigned> |
351 struct IntegerForDigitsAndSign; | 388 struct IntegerForDigitsAndSign; |
352 | 389 |
353 #define INTEGER_FOR_DIGITS_AND_SIGN(I) \ | 390 #define INTEGER_FOR_DIGITS_AND_SIGN(I) \ |
354 template <> \ | 391 template <> \ |
355 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ | 392 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ |
356 std::is_signed<I>::value> { \ | 393 std::is_signed<I>::value> { \ |
(...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
694 static_cast<BigType>(static_cast<L>(lhs)), | 731 static_cast<BigType>(static_cast<L>(lhs)), |
695 static_cast<BigType>(static_cast<R>(rhs))) | 732 static_cast<BigType>(static_cast<R>(rhs))) |
696 // Let the template functions figure it out for mixed types. | 733 // Let the template functions figure it out for mixed types. |
697 : C<L, R>::Test(lhs, rhs); | 734 : C<L, R>::Test(lhs, rhs); |
698 }; | 735 }; |
699 | 736 |
700 } // namespace internal | 737 } // namespace internal |
701 } // namespace base | 738 } // namespace base |
702 | 739 |
703 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 740 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
OLD | NEW |