1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* Cast operations to supplement the built-in casting operations. */
8
9 #ifndef mozilla_Casting_h
10 #define mozilla_Casting_h
11
12 #include "mozilla/Assertions.h"
13 #include "mozilla/TypeTraits.h"
14
15 #include <limits.h>
16
17 namespace mozilla {
18
19 /**
20 * Sets the outparam value of type |To| with the same underlying bit pattern of
21 * |aFrom|.
22 *
23 * |To| and |From| must be types of the same size; be careful of cross-platform
24 * size differences, or this might fail to compile on some but not all
25 * platforms.
26 *
27 * There is also a variant that returns the value directly. In most cases, the
28 * two variants should be identical. However, in the specific case of x86
29 * chips, the behavior differs: returning floating-point values directly is done
30 * through the x87 stack, and x87 loads and stores turn signaling NaNs into
31 * quiet NaNs... silently. Returning floating-point values via outparam,
32 * however, is done entirely within the SSE registers when SSE2 floating-point
33 * is enabled in the compiler, which has semantics-preserving behavior you would
34 * expect.
35 *
36 * If preserving the distinction between signaling NaNs and quiet NaNs is
37 * important to you, you should use the outparam version. In all other cases,
38 * you should use the direct return version.
39 */
40 template <typename To, typename From>
BitwiseCast(const From aFrom,To * aResult)41 inline void BitwiseCast(const From aFrom, To* aResult) {
42 static_assert(sizeof(From) == sizeof(To),
43 "To and From must have the same size");
44 union {
45 From mFrom;
46 To mTo;
47 } u;
48 u.mFrom = aFrom;
49 *aResult = u.mTo;
50 }
51
52 template <typename To, typename From>
BitwiseCast(const From aFrom)53 inline To BitwiseCast(const From aFrom) {
54 To temp;
55 BitwiseCast<To, From>(aFrom, &temp);
56 return temp;
57 }
58
59 namespace detail {
60
61 enum ToSignedness { ToIsSigned, ToIsUnsigned };
62 enum FromSignedness { FromIsSigned, FromIsUnsigned };
63
64 template <typename From, typename To,
65 FromSignedness =
66 IsSigned<From>::value ? FromIsSigned : FromIsUnsigned,
67 ToSignedness = IsSigned<To>::value ? ToIsSigned : ToIsUnsigned>
68 struct BoundsCheckImpl;
69
70 // Implicit conversions on operands to binary operations make this all a bit
71 // hard to verify. Attempt to ease the pain below by *only* comparing values
72 // that are obviously the same type (and will undergo no further conversions),
73 // even when it's not strictly necessary, for explicitness.
74
75 enum UUComparison { FromIsBigger, FromIsNotBigger };
76
77 // Unsigned-to-unsigned range check
78
79 template <typename From, typename To,
80 UUComparison =
81 (sizeof(From) > sizeof(To)) ? FromIsBigger : FromIsNotBigger>
82 struct UnsignedUnsignedCheck;
83
84 template <typename From, typename To>
85 struct UnsignedUnsignedCheck<From, To, FromIsBigger> {
86 public:
87 static bool checkBounds(const From aFrom) { return aFrom <= From(To(-1)); }
88 };
89
90 template <typename From, typename To>
91 struct UnsignedUnsignedCheck<From, To, FromIsNotBigger> {
92 public:
93 static bool checkBounds(const From aFrom) { return true; }
94 };
95
96 template <typename From, typename To>
97 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned> {
98 public:
99 static bool checkBounds(const From aFrom) {
100 return UnsignedUnsignedCheck<From, To>::checkBounds(aFrom);
101 }
102 };
103
104 // Signed-to-unsigned range check
105
106 template <typename From, typename To>
107 struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned> {
108 public:
109 static bool checkBounds(const From aFrom) {
110 if (aFrom < 0) {
111 return false;
112 }
113 if (sizeof(To) >= sizeof(From)) {
114 return true;
115 }
116 return aFrom <= From(To(-1));
117 }
118 };
119
120 // Unsigned-to-signed range check
121
122 enum USComparison { FromIsSmaller, FromIsNotSmaller };
123
124 template <typename From, typename To,
125 USComparison =
126 (sizeof(From) < sizeof(To)) ? FromIsSmaller : FromIsNotSmaller>
127 struct UnsignedSignedCheck;
128
129 template <typename From, typename To>
130 struct UnsignedSignedCheck<From, To, FromIsSmaller> {
131 public:
132 static bool checkBounds(const From aFrom) { return true; }
133 };
134
135 template <typename From, typename To>
136 struct UnsignedSignedCheck<From, To, FromIsNotSmaller> {
137 public:
138 static bool checkBounds(const From aFrom) {
139 const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
140 return aFrom <= From(MaxValue);
141 }
142 };
143
144 template <typename From, typename To>
145 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned> {
146 public:
147 static bool checkBounds(const From aFrom) {
148 return UnsignedSignedCheck<From, To>::checkBounds(aFrom);
149 }
150 };
151
152 // Signed-to-signed range check
153
154 template <typename From, typename To>
155 struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned> {
156 public:
157 static bool checkBounds(const From aFrom) {
158 if (sizeof(From) <= sizeof(To)) {
159 return true;
160 }
161 const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
162 const To MinValue = -MaxValue - To(1);
163 return From(MinValue) <= aFrom && From(aFrom) <= From(MaxValue);
164 }
165 };
166
167 template <typename From, typename To,
168 bool TypesAreIntegral =
169 IsIntegral<From>::value&& IsIntegral<To>::value>
170 class BoundsChecker;
171
172 template <typename From>
173 class BoundsChecker<From, From, true> {
174 public:
175 static bool checkBounds(const From aFrom) { return true; }
176 };
177
178 template <typename From, typename To>
179 class BoundsChecker<From, To, true> {
180 public:
181 static bool checkBounds(const From aFrom) {
182 return BoundsCheckImpl<From, To>::checkBounds(aFrom);
183 }
184 };
185
186 template <typename From, typename To>
187 inline bool IsInBounds(const From aFrom) {
188 return BoundsChecker<From, To>::checkBounds(aFrom);
189 }
190
191 } // namespace detail
192
193 /**
194 * Cast a value of integral type |From| to a value of integral type |To|,
195 * asserting that the cast will be a safe cast per C++ (that is, that |to| is in
196 * the range of values permitted for the type |From|).
197 */
198 template <typename To, typename From>
199 inline To AssertedCast(const From aFrom) {
200 MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
201 return static_cast<To>(aFrom);
202 }
203
204 /**
205 * Cast a value of integral type |From| to a value of integral type |To|,
206 * release asserting that the cast will be a safe cast per C++ (that is, that
207 * |to| is in the range of values permitted for the type |From|).
208 */
209 template <typename To, typename From>
210 inline To ReleaseAssertedCast(const From aFrom) {
211 MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom)));
212 return static_cast<To>(aFrom);
213 }
214
215 } // namespace mozilla
216
217 #endif /* mozilla_Casting_h */
218