blob: 7f05a11b9701bd884d6e41d54a37b85f4ae80ed0 [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
Karl Wibergfd0de7a2017-11-07 11:02:40 +010021enum DstSign { DST_UNSIGNED, DST_SIGNED };
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020022
Karl Wibergfd0de7a2017-11-07 11:02:40 +010023enum SrcSign { SRC_UNSIGNED, SRC_SIGNED };
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020024
Karl Wibergfd0de7a2017-11-07 11:02:40 +010025enum DstRange { OVERLAPS_RANGE, CONTAINS_RANGE };
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020026
27// Helper templates to statically determine if our destination type can contain
28// all values represented by the source type.
29
Karl Wibergfd0de7a2017-11-07 11:02:40 +010030template <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>
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020036struct StaticRangeCheck {};
37
38template <typename Dst, typename Src>
39struct 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.
Karl Wibergfd0de7a2017-11-07 11:02:40 +010043 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;
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020049};
50
51template <typename Dst, typename Src>
52struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> {
Karl Wibergfd0de7a2017-11-07 11:02:40 +010053 static const DstRange value =
54 sizeof(Dst) >= sizeof(Src) ? CONTAINS_RANGE : OVERLAPS_RANGE;
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020055};
56
57template <typename Dst, typename Src>
58struct 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.
Karl Wibergfd0de7a2017-11-07 11:02:40 +010062 static const size_t kDstMaxExponent =
63 DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020064 static const size_t kSrcMaxExponent = sizeof(Src) * 8;
Karl Wibergfd0de7a2017-11-07 11:02:40 +010065 static const DstRange value =
66 kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE;
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020067};
68
69template <typename Dst, typename Src>
70struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> {
71 static const DstRange value = OVERLAPS_RANGE;
72};
73
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020074enum 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) \
Karl Wibergfd0de7a2017-11-07 11:02:40 +010085 RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \
86 ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW))
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020087
88template <typename Dst,
89 typename Src,
Karl Wibergfd0de7a2017-11-07 11:02:40 +010090 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,
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020094 DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value>
95struct 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.
102template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned>
103struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> {
Karl Wibergfd0de7a2017-11-07 11:02:40 +0100104 static RangeCheckResult Check(Src value) { return TYPE_VALID; }
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200105};
106
107// Signed to signed narrowing.
108template <typename Dst, typename Src>
109struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
110 static RangeCheckResult Check(Src value) {
111 typedef std::numeric_limits<Dst> DstLimits;
Karl Wibergfd0de7a2017-11-07 11:02:40 +0100112 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()));
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200119 }
120};
121
122// Unsigned to unsigned narrowing.
123template <typename Dst, typename Src>
124struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
125 static RangeCheckResult Check(Src value) {
126 typedef std::numeric_limits<Dst> DstLimits;
127 return BASE_NUMERIC_RANGE_CHECK_RESULT(
Karl Wibergfd0de7a2017-11-07 11:02:40 +0100128 value <= static_cast<Src>(DstLimits::max()), true);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200129 }
130};
131
132// Unsigned to signed.
133template <typename Dst, typename Src>
134struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
135 static RangeCheckResult Check(Src value) {
136 typedef std::numeric_limits<Dst> DstLimits;
Karl Wibergfd0de7a2017-11-07 11:02:40 +0100137 return sizeof(Dst) > sizeof(Src)
138 ? TYPE_VALID
139 : BASE_NUMERIC_RANGE_CHECK_RESULT(
140 value <= static_cast<Src>(DstLimits::max()), true);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200141 }
142};
143
144// Signed to unsigned.
145template <typename Dst, typename Src>
146struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
147 static RangeCheckResult Check(Src value) {
148 typedef std::numeric_limits<Dst> DstLimits;
149 typedef std::numeric_limits<Src> SrcLimits;
150 // Compare based on max_exponent, which we must compute for integrals.
151 static const size_t kDstMaxExponent = sizeof(Dst) * 8;
Karl Wibergfd0de7a2017-11-07 11:02:40 +0100152 static const size_t kSrcMaxExponent =
153 SrcLimits::is_iec559 ? SrcLimits::max_exponent : (sizeof(Src) * 8 - 1);
154 return (kDstMaxExponent >= kSrcMaxExponent)
155 ? BASE_NUMERIC_RANGE_CHECK_RESULT(true,
156 value >= static_cast<Src>(0))
157 : BASE_NUMERIC_RANGE_CHECK_RESULT(
158 value <= static_cast<Src>(DstLimits::max()),
159 value >= static_cast<Src>(0));
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200160 }
161};
162
163template <typename Dst, typename Src>
164inline RangeCheckResult RangeCheck(Src value) {
165 static_assert(std::numeric_limits<Src>::is_specialized,
166 "argument must be numeric");
167 static_assert(std::numeric_limits<Dst>::is_specialized,
168 "result must be numeric");
169 return RangeCheckImpl<Dst, Src>::Check(value);
170}
171
172} // namespace internal
173} // namespace rtc
Tommid44c0772016-03-11 17:12:32 -0800174
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200175#endif // RTC_BASE_SAFE_CONVERSIONS_IMPL_H_