Home | History | Annotate | Download | only in base
      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>
    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>
    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