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
18namespace 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  COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized,
179                 argument_must_be_numeric);
180  COMPILE_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
187
188#endif  // WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
189