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