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 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 | 129 |
130 // Signed to unsigned: Dst cannot be statically determined to contain Src. | 130 // Signed to unsigned: Dst cannot be statically determined to contain Src. |
131 template <typename Dst, typename Src> | 131 template <typename Dst, typename Src> |
132 struct StaticDstRangeRelationToSrcRange<Dst, | 132 struct StaticDstRangeRelationToSrcRange<Dst, |
133 Src, | 133 Src, |
134 INTEGER_REPRESENTATION_UNSIGNED, | 134 INTEGER_REPRESENTATION_UNSIGNED, |
135 INTEGER_REPRESENTATION_SIGNED> { | 135 INTEGER_REPRESENTATION_SIGNED> { |
136 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; | 136 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; |
137 }; | 137 }; |
138 | 138 |
139 enum RangeConstraint { | |
140 RANGE_VALID = 0x0, // Value can be represented by the destination type. | |
141 RANGE_UNDERFLOW = 0x1, // Value would underflow. | |
142 RANGE_OVERFLOW = 0x2, // Value would overflow. | |
143 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). | |
144 }; | |
145 | |
146 // This class wraps the range constraints as separate booleans so the compiler | 139 // This class wraps the range constraints as separate booleans so the compiler |
147 // can identify constants and eliminate unused code paths. | 140 // can identify constants and eliminate unused code paths. |
148 class RangeCheck { | 141 class RangeCheck { |
149 public: | 142 public: |
150 constexpr RangeCheck(bool is_in_upper_bound, bool is_in_lower_bound) | 143 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) |
151 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} | 144 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} |
152 constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {} | 145 constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {} |
153 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } | 146 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } |
154 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } | 147 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } |
155 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } | 148 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } |
156 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } | 149 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } |
157 constexpr bool IsOverflowFlagSet() const { return is_overflow_; } | 150 constexpr bool IsOverflowFlagSet() const { return is_overflow_; } |
158 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } | 151 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } |
159 | 152 constexpr bool operator==(const RangeCheck rhs) const { |
160 // These are some wrappers to make the tests a bit cleaner. | 153 return is_underflow_ == rhs.is_underflow_ && |
161 constexpr operator RangeConstraint() const { | 154 is_overflow_ == rhs.is_overflow_; |
162 return static_cast<RangeConstraint>(static_cast<int>(is_overflow_) << 1 | | |
163 static_cast<int>(is_underflow_)); | |
164 } | 155 } |
165 constexpr bool operator==(const RangeConstraint rhs) const { | 156 constexpr bool operator!=(const RangeCheck rhs) const { |
166 return rhs == static_cast<RangeConstraint>(*this); | 157 return !(*this == rhs); |
167 } | 158 } |
168 | 159 |
169 private: | 160 private: |
170 // Do not change the order of these member variables. The integral conversion | 161 // Do not change the order of these member variables. The integral conversion |
171 // optimization depends on this exact order. | 162 // optimization depends on this exact order. |
172 const bool is_underflow_ : 1; | 163 const bool is_underflow_; |
173 const bool is_overflow_ : 1; | 164 const bool is_overflow_; |
174 }; | 165 }; |
175 | 166 |
176 // The following helper template addresses a corner case in range checks for | 167 // The following helper template addresses a corner case in range checks for |
177 // conversion from a floating-point type to an integral type of smaller range | 168 // conversion from a floating-point type to an integral type of smaller range |
178 // but larger precision (e.g. float -> unsigned). The problem is as follows: | 169 // but larger precision (e.g. float -> unsigned). The problem is as follows: |
179 // 1. Integral maximum is always one less than a power of two, so it must be | 170 // 1. Integral maximum is always one less than a power of two, so it must be |
180 // truncated to fit the mantissa of the floating point. The direction of | 171 // truncated to fit the mantissa of the floating point. The direction of |
181 // rounding is implementation defined, but by default it's always IEEE | 172 // rounding is implementation defined, but by default it's always IEEE |
182 // floats, which round to nearest and thus result in a value of larger | 173 // floats, which round to nearest and thus result in a value of larger |
183 // magnitude than the integral value. | 174 // magnitude than the integral value. |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 struct DstRangeRelationToSrcRangeImpl<Dst, | 249 struct DstRangeRelationToSrcRangeImpl<Dst, |
259 Src, | 250 Src, |
260 Bounds, | 251 Bounds, |
261 DstSign, | 252 DstSign, |
262 SrcSign, | 253 SrcSign, |
263 NUMERIC_RANGE_CONTAINED> { | 254 NUMERIC_RANGE_CONTAINED> { |
264 static constexpr RangeCheck Check(Src value) { | 255 static constexpr RangeCheck Check(Src value) { |
265 using SrcLimits = std::numeric_limits<Src>; | 256 using SrcLimits = std::numeric_limits<Src>; |
266 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 257 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
267 return RangeCheck( | 258 return RangeCheck( |
| 259 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || |
| 260 static_cast<Dst>(value) >= DstLimits::lowest(), |
268 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || | 261 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || |
269 static_cast<Dst>(value) <= DstLimits::max(), | 262 static_cast<Dst>(value) <= DstLimits::max()); |
270 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || | |
271 static_cast<Dst>(value) >= DstLimits::lowest()); | |
272 } | 263 } |
273 }; | 264 }; |
274 | 265 |
275 // Signed to signed narrowing: Both the upper and lower boundaries may be | 266 // Signed to signed narrowing: Both the upper and lower boundaries may be |
276 // exceeded for standard limits. | 267 // exceeded for standard limits. |
277 template <typename Dst, typename Src, template <typename> class Bounds> | 268 template <typename Dst, typename Src, template <typename> class Bounds> |
278 struct DstRangeRelationToSrcRangeImpl<Dst, | 269 struct DstRangeRelationToSrcRangeImpl<Dst, |
279 Src, | 270 Src, |
280 Bounds, | 271 Bounds, |
281 INTEGER_REPRESENTATION_SIGNED, | 272 INTEGER_REPRESENTATION_SIGNED, |
282 INTEGER_REPRESENTATION_SIGNED, | 273 INTEGER_REPRESENTATION_SIGNED, |
283 NUMERIC_RANGE_NOT_CONTAINED> { | 274 NUMERIC_RANGE_NOT_CONTAINED> { |
284 static constexpr RangeCheck Check(Src value) { | 275 static constexpr RangeCheck Check(Src value) { |
285 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 276 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
286 return RangeCheck(value <= DstLimits::max(), value >= DstLimits::lowest()); | 277 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); |
287 } | 278 } |
288 }; | 279 }; |
289 | 280 |
290 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for | 281 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for |
291 // standard limits. | 282 // standard limits. |
292 template <typename Dst, typename Src, template <typename> class Bounds> | 283 template <typename Dst, typename Src, template <typename> class Bounds> |
293 struct DstRangeRelationToSrcRangeImpl<Dst, | 284 struct DstRangeRelationToSrcRangeImpl<Dst, |
294 Src, | 285 Src, |
295 Bounds, | 286 Bounds, |
296 INTEGER_REPRESENTATION_UNSIGNED, | 287 INTEGER_REPRESENTATION_UNSIGNED, |
297 INTEGER_REPRESENTATION_UNSIGNED, | 288 INTEGER_REPRESENTATION_UNSIGNED, |
298 NUMERIC_RANGE_NOT_CONTAINED> { | 289 NUMERIC_RANGE_NOT_CONTAINED> { |
299 static constexpr RangeCheck Check(Src value) { | 290 static constexpr RangeCheck Check(Src value) { |
300 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 291 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
301 return RangeCheck( | 292 return RangeCheck( |
302 value <= DstLimits::max(), | 293 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), |
303 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest()); | 294 value <= DstLimits::max()); |
304 } | 295 } |
305 }; | 296 }; |
306 | 297 |
307 // Unsigned to signed: Only the upper bound can be exceeded for standard limits. | 298 // Unsigned to signed: Only the upper bound can be exceeded for standard limits. |
308 template <typename Dst, typename Src, template <typename> class Bounds> | 299 template <typename Dst, typename Src, template <typename> class Bounds> |
309 struct DstRangeRelationToSrcRangeImpl<Dst, | 300 struct DstRangeRelationToSrcRangeImpl<Dst, |
310 Src, | 301 Src, |
311 Bounds, | 302 Bounds, |
312 INTEGER_REPRESENTATION_SIGNED, | 303 INTEGER_REPRESENTATION_SIGNED, |
313 INTEGER_REPRESENTATION_UNSIGNED, | 304 INTEGER_REPRESENTATION_UNSIGNED, |
314 NUMERIC_RANGE_NOT_CONTAINED> { | 305 NUMERIC_RANGE_NOT_CONTAINED> { |
315 static constexpr RangeCheck Check(Src value) { | 306 static constexpr RangeCheck Check(Src value) { |
316 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 307 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
317 using Promotion = decltype(Src() + Dst()); | 308 using Promotion = decltype(Src() + Dst()); |
318 return RangeCheck(static_cast<Promotion>(value) <= | 309 return RangeCheck(DstLimits::lowest() <= Dst(0) || |
319 static_cast<Promotion>(DstLimits::max()), | |
320 DstLimits::lowest() <= Dst(0) || | |
321 static_cast<Promotion>(value) >= | 310 static_cast<Promotion>(value) >= |
322 static_cast<Promotion>(DstLimits::lowest())); | 311 static_cast<Promotion>(DstLimits::lowest()), |
| 312 static_cast<Promotion>(value) <= |
| 313 static_cast<Promotion>(DstLimits::max())); |
323 } | 314 } |
324 }; | 315 }; |
325 | 316 |
326 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, | 317 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, |
327 // and any negative value exceeds the lower boundary for standard limits. | 318 // and any negative value exceeds the lower boundary for standard limits. |
328 template <typename Dst, typename Src, template <typename> class Bounds> | 319 template <typename Dst, typename Src, template <typename> class Bounds> |
329 struct DstRangeRelationToSrcRangeImpl<Dst, | 320 struct DstRangeRelationToSrcRangeImpl<Dst, |
330 Src, | 321 Src, |
331 Bounds, | 322 Bounds, |
332 INTEGER_REPRESENTATION_UNSIGNED, | 323 INTEGER_REPRESENTATION_UNSIGNED, |
333 INTEGER_REPRESENTATION_SIGNED, | 324 INTEGER_REPRESENTATION_SIGNED, |
334 NUMERIC_RANGE_NOT_CONTAINED> { | 325 NUMERIC_RANGE_NOT_CONTAINED> { |
335 static constexpr RangeCheck Check(Src value) { | 326 static constexpr RangeCheck Check(Src value) { |
336 using SrcLimits = std::numeric_limits<Src>; | 327 using SrcLimits = std::numeric_limits<Src>; |
337 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 328 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
338 using Promotion = decltype(Src() + Dst()); | 329 using Promotion = decltype(Src() + Dst()); |
339 return RangeCheck( | 330 return RangeCheck( |
| 331 value >= Src(0) && (DstLimits::lowest() == 0 || |
| 332 static_cast<Dst>(value) >= DstLimits::lowest()), |
340 static_cast<Promotion>(SrcLimits::max()) <= | 333 static_cast<Promotion>(SrcLimits::max()) <= |
341 static_cast<Promotion>(DstLimits::max()) || | 334 static_cast<Promotion>(DstLimits::max()) || |
342 static_cast<Promotion>(value) <= | 335 static_cast<Promotion>(value) <= |
343 static_cast<Promotion>(DstLimits::max()), | 336 static_cast<Promotion>(DstLimits::max())); |
344 value >= Src(0) && (DstLimits::lowest() == 0 || | |
345 static_cast<Dst>(value) >= DstLimits::lowest())); | |
346 } | 337 } |
347 }; | 338 }; |
348 | 339 |
349 template <typename Dst, | 340 template <typename Dst, |
350 template <typename> class Bounds = std::numeric_limits, | 341 template <typename> class Bounds = std::numeric_limits, |
351 typename Src> | 342 typename Src> |
352 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { | 343 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { |
353 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); | 344 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); |
354 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 345 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); |
355 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); | 346 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); |
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
608 static const bool value = | 599 static const bool value = |
609 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && | 600 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && |
610 (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict); | 601 (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict); |
611 }; | 602 }; |
612 | 603 |
613 template <typename L, typename R> | 604 template <typename L, typename R> |
614 constexpr bool IsLessImpl(const L lhs, | 605 constexpr bool IsLessImpl(const L lhs, |
615 const R rhs, | 606 const R rhs, |
616 const RangeCheck l_range, | 607 const RangeCheck l_range, |
617 const RangeCheck r_range) { | 608 const RangeCheck r_range) { |
618 return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || | 609 return l_range.IsUnderflow() || r_range.IsOverflow() || |
619 (l_range == r_range && | 610 (l_range == r_range && |
620 static_cast<decltype(lhs + rhs)>(lhs) < | 611 static_cast<decltype(lhs + rhs)>(lhs) < |
621 static_cast<decltype(lhs + rhs)>(rhs)); | 612 static_cast<decltype(lhs + rhs)>(rhs)); |
622 } | 613 } |
623 | 614 |
624 template <typename L, typename R> | 615 template <typename L, typename R> |
625 struct IsLess { | 616 struct IsLess { |
626 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 617 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
627 "Types must be numeric."); | 618 "Types must be numeric."); |
628 static constexpr bool Test(const L lhs, const R rhs) { | 619 static constexpr bool Test(const L lhs, const R rhs) { |
629 return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), | 620 return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
630 DstRangeRelationToSrcRange<L>(rhs)); | 621 DstRangeRelationToSrcRange<L>(rhs)); |
631 } | 622 } |
632 }; | 623 }; |
633 | 624 |
634 template <typename L, typename R> | 625 template <typename L, typename R> |
635 constexpr bool IsLessOrEqualImpl(const L lhs, | 626 constexpr bool IsLessOrEqualImpl(const L lhs, |
636 const R rhs, | 627 const R rhs, |
637 const RangeCheck l_range, | 628 const RangeCheck l_range, |
638 const RangeCheck r_range) { | 629 const RangeCheck r_range) { |
639 return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || | 630 return l_range.IsUnderflow() || r_range.IsOverflow() || |
640 (l_range == r_range && | 631 (l_range == r_range && |
641 static_cast<decltype(lhs + rhs)>(lhs) <= | 632 static_cast<decltype(lhs + rhs)>(lhs) <= |
642 static_cast<decltype(lhs + rhs)>(rhs)); | 633 static_cast<decltype(lhs + rhs)>(rhs)); |
643 } | 634 } |
644 | 635 |
645 template <typename L, typename R> | 636 template <typename L, typename R> |
646 struct IsLessOrEqual { | 637 struct IsLessOrEqual { |
647 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 638 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
648 "Types must be numeric."); | 639 "Types must be numeric."); |
649 static constexpr bool Test(const L lhs, const R rhs) { | 640 static constexpr bool Test(const L lhs, const R rhs) { |
650 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), | 641 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
651 DstRangeRelationToSrcRange<L>(rhs)); | 642 DstRangeRelationToSrcRange<L>(rhs)); |
652 } | 643 } |
653 }; | 644 }; |
654 | 645 |
655 template <typename L, typename R> | 646 template <typename L, typename R> |
656 constexpr bool IsGreaterImpl(const L lhs, | 647 constexpr bool IsGreaterImpl(const L lhs, |
657 const R rhs, | 648 const R rhs, |
658 const RangeCheck l_range, | 649 const RangeCheck l_range, |
659 const RangeCheck r_range) { | 650 const RangeCheck r_range) { |
660 return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || | 651 return l_range.IsOverflow() || r_range.IsUnderflow() || |
661 (l_range == r_range && | 652 (l_range == r_range && |
662 static_cast<decltype(lhs + rhs)>(lhs) > | 653 static_cast<decltype(lhs + rhs)>(lhs) > |
663 static_cast<decltype(lhs + rhs)>(rhs)); | 654 static_cast<decltype(lhs + rhs)>(rhs)); |
664 } | 655 } |
665 | 656 |
666 template <typename L, typename R> | 657 template <typename L, typename R> |
667 struct IsGreater { | 658 struct IsGreater { |
668 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 659 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
669 "Types must be numeric."); | 660 "Types must be numeric."); |
670 static constexpr bool Test(const L lhs, const R rhs) { | 661 static constexpr bool Test(const L lhs, const R rhs) { |
671 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), | 662 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
672 DstRangeRelationToSrcRange<L>(rhs)); | 663 DstRangeRelationToSrcRange<L>(rhs)); |
673 } | 664 } |
674 }; | 665 }; |
675 | 666 |
676 template <typename L, typename R> | 667 template <typename L, typename R> |
677 constexpr bool IsGreaterOrEqualImpl(const L lhs, | 668 constexpr bool IsGreaterOrEqualImpl(const L lhs, |
678 const R rhs, | 669 const R rhs, |
679 const RangeCheck l_range, | 670 const RangeCheck l_range, |
680 const RangeCheck r_range) { | 671 const RangeCheck r_range) { |
681 return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || | 672 return l_range.IsOverflow() || r_range.IsUnderflow() || |
682 (l_range == r_range && | 673 (l_range == r_range && |
683 static_cast<decltype(lhs + rhs)>(lhs) >= | 674 static_cast<decltype(lhs + rhs)>(lhs) >= |
684 static_cast<decltype(lhs + rhs)>(rhs)); | 675 static_cast<decltype(lhs + rhs)>(rhs)); |
685 } | 676 } |
686 | 677 |
687 template <typename L, typename R> | 678 template <typename L, typename R> |
688 struct IsGreaterOrEqual { | 679 struct IsGreaterOrEqual { |
689 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 680 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
690 "Types must be numeric."); | 681 "Types must be numeric."); |
691 static constexpr bool Test(const L lhs, const R rhs) { | 682 static constexpr bool Test(const L lhs, const R rhs) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
732 static_cast<BigType>(static_cast<L>(lhs)), | 723 static_cast<BigType>(static_cast<L>(lhs)), |
733 static_cast<BigType>(static_cast<R>(rhs))) | 724 static_cast<BigType>(static_cast<R>(rhs))) |
734 // Let the template functions figure it out for mixed types. | 725 // Let the template functions figure it out for mixed types. |
735 : C<L, R>::Test(lhs, rhs); | 726 : C<L, R>::Test(lhs, rhs); |
736 }; | 727 }; |
737 | 728 |
738 } // namespace internal | 729 } // namespace internal |
739 } // namespace base | 730 } // namespace base |
740 | 731 |
741 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 732 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
OLD | NEW |