1 // Copyright 2013 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.
5 #ifndef BASE_SAFE_NUMERICS_H_
6 #define BASE_SAFE_NUMERICS_H_
10 #include "base/logging.h"
15 template <bool SameSize
, bool DestLarger
,
16 bool DestIsSigned
, bool SourceIsSigned
>
17 struct IsValidNumericCastImpl
;
19 #define BASE_NUMERIC_CAST_CASE_SPECIALIZATION(A, B, C, D, Code) \
20 template <> struct IsValidNumericCastImpl<A, B, C, D> { \
21 template <class Source, class DestBounds> static inline bool Test( \
22 Source source, DestBounds min, DestBounds max) { \
27 #define BASE_NUMERIC_CAST_CASE_SAME_SIZE(DestSigned, SourceSigned, Code) \
28 BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
29 true, true, DestSigned, SourceSigned, Code); \
30 BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
31 true, false, DestSigned, SourceSigned, Code)
33 #define BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(DestSigned, SourceSigned, Code) \
34 BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
35 false, false, DestSigned, SourceSigned, Code); \
37 #define BASE_NUMERIC_CAST_CASE_DEST_LARGER(DestSigned, SourceSigned, Code) \
38 BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
39 false, true, DestSigned, SourceSigned, Code); \
41 // The three top level cases are:
45 // And for each of those three cases, we handle the 4 different possibilities
46 // of signed and unsigned. This gives 12 cases to handle, which we enumerate
49 // The last argument in each of the macros is the actual comparison code. It
50 // has three arguments available, source (the value), and min/max which are
51 // the ranges of the destination.
54 // These are the cases where both types have the same size.
57 BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, true, true);
59 BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, false, true);
60 // Dest unsigned, Source signed.
61 BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, true, source
>= 0);
62 // Dest signed, Source unsigned.
63 // This cast is OK because Dest's max must be less than Source's.
64 BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, false,
65 source
<= static_cast<Source
>(max
));
68 // These are the cases where Source is larger.
71 BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, false, source
<= max
);
73 BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, true,
74 source
>= min
&& source
<= max
);
75 // Dest is unsigned, Source is signed.
76 BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, true,
77 source
>= 0 && source
<= max
);
78 // Dest is signed, Source is unsigned.
79 // This cast is OK because Dest's max must be less than Source's.
80 BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, false,
81 source
<= static_cast<Source
>(max
));
84 // These are the cases where Dest is larger.
87 BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, false, true);
89 BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, true, true);
90 // Dest is unsigned, Source is signed.
91 BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, true, source
>= 0);
92 // Dest is signed, Source is unsigned.
93 BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, false, true);
95 #undef BASE_NUMERIC_CAST_CASE_SPECIALIZATION
96 #undef BASE_NUMERIC_CAST_CASE_SAME_SIZE
97 #undef BASE_NUMERIC_CAST_CASE_SOURCE_LARGER
98 #undef BASE_NUMERIC_CAST_CASE_DEST_LARGER
101 // The main test for whether the conversion will under or overflow.
102 template <class Dest
, class Source
>
103 inline bool IsValidNumericCast(Source source
) {
104 typedef std::numeric_limits
<Source
> SourceLimits
;
105 typedef std::numeric_limits
<Dest
> DestLimits
;
106 COMPILE_ASSERT(SourceLimits::is_specialized
, argument_must_be_numeric
);
107 COMPILE_ASSERT(SourceLimits::is_integer
, argument_must_be_integral
);
108 COMPILE_ASSERT(DestLimits::is_specialized
, result_must_be_numeric
);
109 COMPILE_ASSERT(DestLimits::is_integer
, result_must_be_integral
);
111 return IsValidNumericCastImpl
<
112 sizeof(Dest
) == sizeof(Source
),
113 (sizeof(Dest
) > sizeof(Source
)),
114 DestLimits::is_signed
,
115 SourceLimits::is_signed
>::Test(
121 } // namespace internal
123 // checked_numeric_cast<> is analogous to static_cast<> for numeric types,
124 // except that it CHECKs that the specified numeric conversion will not
125 // overflow or underflow. Floating point arguments are not currently allowed
126 // (this is COMPILE_ASSERTd), though this could be supported if necessary.
127 template <class Dest
, class Source
>
128 inline Dest
checked_numeric_cast(Source source
) {
129 CHECK(internal::IsValidNumericCast
<Dest
>(source
));
130 return static_cast<Dest
>(source
);
135 #endif // BASE_SAFE_NUMERICS_H_