• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #ifndef BASE_SAFE_NUMERICS_H_
6 #define BASE_SAFE_NUMERICS_H_
7 
8 #include <limits>
9 
10 #include "base/logging.h"
11 
12 namespace base {
13 namespace internal {
14 
15 template <bool SameSize, bool DestLarger,
16           bool DestIsSigned, bool SourceIsSigned>
17 struct IsValidNumericCastImpl;
18 
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) { \
23     return Code; \
24   } \
25 }
26 
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)
32 
33 #define BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(DestSigned, SourceSigned, Code) \
34   BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
35       false, false, DestSigned, SourceSigned, Code); \
36 
37 #define BASE_NUMERIC_CAST_CASE_DEST_LARGER(DestSigned, SourceSigned, Code) \
38   BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
39       false, true, DestSigned, SourceSigned, Code); \
40 
41 // The three top level cases are:
42 // - Same size
43 // - Source larger
44 // - Dest larger
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
47 // below.
48 //
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.
52 
53 
54 // These are the cases where both types have the same size.
55 
56 // Both signed.
57 BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, true, true);
58 // Both unsigned.
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));
66 
67 
68 // These are the cases where Source is larger.
69 
70 // Both unsigned.
71 BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, false, source <= max);
72 // Both signed.
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));
82 
83 
84 // These are the cases where Dest is larger.
85 
86 // Both unsigned.
87 BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, false, true);
88 // Both signed.
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);
94 
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
99 
100 
101 // The main test for whether the conversion will under or overflow.
102 template <class Dest, class Source>
IsValidNumericCast(Source 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);
110 
111   return IsValidNumericCastImpl<
112       sizeof(Dest) == sizeof(Source),
113       (sizeof(Dest) > sizeof(Source)),
114       DestLimits::is_signed,
115       SourceLimits::is_signed>::Test(
116           source,
117           DestLimits::min(),
118           DestLimits::max());
119 }
120 
121 }  // namespace internal
122 
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>
checked_numeric_cast(Source source)128 inline Dest checked_numeric_cast(Source source) {
129   CHECK(internal::IsValidNumericCast<Dest>(source));
130   return static_cast<Dest>(source);
131 }
132 
133 }  // namespace base
134 
135 #endif  // BASE_SAFE_NUMERICS_H_
136