Line data Source code
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 0 : 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 0 : 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 0 : static RangeCheckResult Check(Src value) {
140 : typedef std::numeric_limits<Dst> DstLimits;
141 0 : 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 0 : static RangeCheckResult Check(Src value) {
150 : typedef std::numeric_limits<Dst> DstLimits;
151 : return sizeof(Dst) > sizeof(Src) ? TYPE_VALID :
152 0 : 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 0 : 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 0 : BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast<Src>(0)) :
170 0 : 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 0 : 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 0 : return RangeCheckImpl<Dst, Src>::Check(value);
183 : }
184 :
185 : } // namespace internal
186 : } // namespace rtc
187 :
188 : #endif // WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
|