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