blob: f9f625687f9277c51c95cb331cf6cbe5f68824ad [file] [log] [blame]
Tommid44c0772016-03-11 17:12:32 -08001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#ifndef RTC_BASE_SAFE_CONVERSIONS_IMPL_H_
14#define RTC_BASE_SAFE_CONVERSIONS_IMPL_H_
Tommid44c0772016-03-11 17:12:32 -080015
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020016#include <limits>
Tommid44c0772016-03-11 17:12:32 -080017
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020018namespace rtc {
19namespace internal {
20
21enum DstSign {
22 DST_UNSIGNED,
23 DST_SIGNED
24};
25
26enum SrcSign {
27 SRC_UNSIGNED,
28 SRC_SIGNED
29};
30
31enum 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
39template <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>
44struct StaticRangeCheck {};
45
46template <typename Dst, typename Src>
47struct 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
61template <typename Dst, typename Src>
62struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> {
63 static const DstRange value = sizeof(Dst) >= sizeof(Src) ?
64 CONTAINS_RANGE : OVERLAPS_RANGE;
65};
66
67template <typename Dst, typename Src>
68struct 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
80template <typename Dst, typename Src>
81struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> {
82 static const DstRange value = OVERLAPS_RANGE;
83};
84
85
86enum 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
100template <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>
107struct 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.
114template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned>
115struct 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.
122template <typename Dst, typename Src>
123struct 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.
137template <typename Dst, typename Src>
138struct 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.
147template <typename Dst, typename Src>
148struct 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.
158template <typename Dst, typename Src>
159struct 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
176template <typename Dst, typename Src>
177inline 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
Tommid44c0772016-03-11 17:12:32 -0800187
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200188#endif // RTC_BASE_SAFE_CONVERSIONS_IMPL_H_