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