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

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

Issue 2614073002: Cleanup RangeCheck class and dependencies (Closed)
Patch Set: remove integer saturation specialization Created 3 years, 11 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
« no previous file with comments | « base/numerics/safe_conversions.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_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
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
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
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
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_
OLDNEW
« no previous file with comments | « base/numerics/safe_conversions.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698