Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 // Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h.
     12 
     13 #ifndef WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
     14 #define WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
     15 
     16 #include <limits>
     17 
     18 namespace rtc {
     19 namespace internal {
     20 
     21 enum DstSign {
     22   DST_UNSIGNED,
     23   DST_SIGNED
     24 };
     25 
     26 enum SrcSign {
     27   SRC_UNSIGNED,
     28   SRC_SIGNED
     29 };
     30 
     31 enum DstRange {
     32   OVERLAPS_RANGE,
     33   CONTAINS_RANGE
     34 };
     35 
     36 // Helper templates to statically determine if our destination type can contain
     37 // all values represented by the source type.
     38 
     39 template <typename Dst, typename Src,
     40           DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ?
     41                                 DST_SIGNED : DST_UNSIGNED,
     42           SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ?
     43                                 SRC_SIGNED : SRC_UNSIGNED>
     44 struct StaticRangeCheck {};
     45 
     46 template <typename Dst, typename Src>
     47 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> {
     48   typedef std::numeric_limits<Dst> DstLimits;
     49   typedef std::numeric_limits<Src> SrcLimits;
     50   // Compare based on max_exponent, which we must compute for integrals.
     51   static const size_t kDstMaxExponent = DstLimits::is_iec559 ?
     52                                         DstLimits::max_exponent :
     53                                         (sizeof(Dst) * 8 - 1);
     54   static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ?
     55                                         SrcLimits::max_exponent :
     56                                         (sizeof(Src) * 8 - 1);
     57   static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ?
     58                                 CONTAINS_RANGE : OVERLAPS_RANGE;
     59 };
     60 
     61 template <typename Dst, typename Src>
     62 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> {
     63   static const DstRange value = sizeof(Dst) >= sizeof(Src) ?
     64                                 CONTAINS_RANGE : OVERLAPS_RANGE;
     65 };
     66 
     67 template <typename Dst, typename Src>
     68 struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> {
     69   typedef std::numeric_limits<Dst> DstLimits;
     70   typedef std::numeric_limits<Src> SrcLimits;
     71   // Compare based on max_exponent, which we must compute for integrals.
     72   static const size_t kDstMaxExponent = DstLimits::is_iec559 ?
     73                                         DstLimits::max_exponent :
     74                                         (sizeof(Dst) * 8 - 1);
     75   static const size_t kSrcMaxExponent = sizeof(Src) * 8;
     76   static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ?
     77                                 CONTAINS_RANGE : OVERLAPS_RANGE;
     78 };
     79 
     80 template <typename Dst, typename Src>
     81 struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> {
     82   static const DstRange value = OVERLAPS_RANGE;
     83 };
     84 
     85 
     86 enum RangeCheckResult {
     87   TYPE_VALID = 0,      // Value can be represented by the destination type.
     88   TYPE_UNDERFLOW = 1,  // Value would overflow.
     89   TYPE_OVERFLOW = 2,   // Value would underflow.
     90   TYPE_INVALID = 3     // Source value is invalid (i.e. NaN).
     91 };
     92 
     93 // This macro creates a RangeCheckResult from an upper and lower bound
     94 // check by taking advantage of the fact that only NaN can be out of range in
     95 // both directions at once.
     96 #define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \
     97     RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \
     98                             ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW))
     99 
    100 template <typename Dst,
    101           typename Src,
    102           DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ?
    103                                 DST_SIGNED : DST_UNSIGNED,
    104           SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ?
    105                                 SRC_SIGNED : SRC_UNSIGNED,
    106           DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value>
    107 struct RangeCheckImpl {};
    108 
    109 // The following templates are for ranges that must be verified at runtime. We
    110 // split it into checks based on signedness to avoid confusing casts and
    111 // compiler warnings on signed an unsigned comparisons.
    112 
    113 // Dst range always contains the result: nothing to check.
    114 template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned>
    115 struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> {
    116   static RangeCheckResult Check(Src value) {
    117     return TYPE_VALID;
    118   }
    119 };
    120 
    121 // Signed to signed narrowing.
    122 template <typename Dst, typename Src>
    123 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
    124   static RangeCheckResult Check(Src value) {
    125     typedef std::numeric_limits<Dst> DstLimits;
    126     return DstLimits::is_iec559 ?
    127            BASE_NUMERIC_RANGE_CHECK_RESULT(
    128                value <= static_cast<Src>(DstLimits::max()),
    129                value >= static_cast<Src>(DstLimits::max() * -1)) :
    130            BASE_NUMERIC_RANGE_CHECK_RESULT(
    131                value <= static_cast<Src>(DstLimits::max()),
    132                value >= static_cast<Src>(DstLimits::min()));
    133   }
    134 };
    135 
    136 // Unsigned to unsigned narrowing.
    137 template <typename Dst, typename Src>
    138 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
    139   static RangeCheckResult Check(Src value) {
    140     typedef std::numeric_limits<Dst> DstLimits;
    141     return BASE_NUMERIC_RANGE_CHECK_RESULT(
    142                value <= static_cast<Src>(DstLimits::max()), true);
    143   }
    144 };
    145 
    146 // Unsigned to signed.
    147 template <typename Dst, typename Src>
    148 struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
    149   static RangeCheckResult Check(Src value) {
    150     typedef std::numeric_limits<Dst> DstLimits;
    151     return sizeof(Dst) > sizeof(Src) ? TYPE_VALID :
    152            BASE_NUMERIC_RANGE_CHECK_RESULT(
    153                value <= static_cast<Src>(DstLimits::max()), true);
    154   }
    155 };
    156 
    157 // Signed to unsigned.
    158 template <typename Dst, typename Src>
    159 struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
    160   static RangeCheckResult Check(Src value) {
    161     typedef std::numeric_limits<Dst> DstLimits;
    162     typedef std::numeric_limits<Src> SrcLimits;
    163     // Compare based on max_exponent, which we must compute for integrals.
    164     static const size_t kDstMaxExponent = sizeof(Dst) * 8;
    165     static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ?
    166                                           SrcLimits::max_exponent :
    167                                           (sizeof(Src) * 8 - 1);
    168     return (kDstMaxExponent >= kSrcMaxExponent) ?
    169            BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast<Src>(0)) :
    170            BASE_NUMERIC_RANGE_CHECK_RESULT(
    171                value <= static_cast<Src>(DstLimits::max()),
    172                value >= static_cast<Src>(0));
    173   }
    174 };
    175 
    176 template <typename Dst, typename Src>
    177 inline RangeCheckResult RangeCheck(Src value) {
    178   static_assert(std::numeric_limits<Src>::is_specialized,
    179                 "argument must be numeric");
    180   static_assert(std::numeric_limits<Dst>::is_specialized,
    181                 "result must be numeric");
    182   return RangeCheckImpl<Dst, Src>::Check(value);
    183 }
    184 
    185 }  // namespace internal
    186 }  // namespace rtc
    187 
    188 #endif  // WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
    189