Line data Source code
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 <cstring>
16 : #include <limits.h>
17 : #include <type_traits>
18 :
19 : namespace mozilla {
20 :
21 : /**
22 : * Sets the outparam value of type |To| with the same underlying bit pattern of
23 : * |aFrom|.
24 : *
25 : * |To| and |From| must be types of the same size; be careful of cross-platform
26 : * size differences, or this might fail to compile on some but not all
27 : * platforms.
28 : *
29 : * There is also a variant that returns the value directly. In most cases, the
30 : * two variants should be identical. However, in the specific case of x86
31 : * chips, the behavior differs: returning floating-point values directly is done
32 : * through the x87 stack, and x87 loads and stores turn signaling NaNs into
33 : * quiet NaNs... silently. Returning floating-point values via outparam,
34 : * however, is done entirely within the SSE registers when SSE2 floating-point
35 : * is enabled in the compiler, which has semantics-preserving behavior you would
36 : * expect.
37 : *
38 : * If preserving the distinction between signaling NaNs and quiet NaNs is
39 : * important to you, you should use the outparam version. In all other cases,
40 : * you should use the direct return version.
41 : */
42 : template<typename To, typename From>
43 : inline void
44 32057 : BitwiseCast(const From aFrom, To* aResult)
45 : {
46 : static_assert(sizeof(From) == sizeof(To),
47 : "To and From must have the same size");
48 :
49 : // We could maybe downgrade these to std::is_trivially_copyable, but the
50 : // various STLs we use don't all provide it.
51 : static_assert(std::is_trivial<From>::value,
52 : "shouldn't bitwise-copy a type having non-trivial "
53 : "initialization");
54 : static_assert(std::is_trivial<To>::value,
55 : "shouldn't bitwise-copy a type having non-trivial "
56 : "initialization");
57 :
58 171422 : std::memcpy(static_cast<void*>(aResult),
59 : static_cast<const void*>(&aFrom),
60 : sizeof(From));
61 32057 : }
62 :
63 : template<typename To, typename From>
64 : inline To
65 32056 : BitwiseCast(const From aFrom)
66 : {
67 : To temp;
68 170842 : BitwiseCast<To, From>(aFrom, &temp);
69 32057 : return temp;
70 : }
71 :
72 : namespace detail {
73 :
74 : enum ToSignedness { ToIsSigned, ToIsUnsigned };
75 : enum FromSignedness { FromIsSigned, FromIsUnsigned };
76 :
77 : template<typename From,
78 : typename To,
79 : FromSignedness = IsSigned<From>::value ? FromIsSigned : FromIsUnsigned,
80 : ToSignedness = IsSigned<To>::value ? ToIsSigned : ToIsUnsigned>
81 : struct BoundsCheckImpl;
82 :
83 : // Implicit conversions on operands to binary operations make this all a bit
84 : // hard to verify. Attempt to ease the pain below by *only* comparing values
85 : // that are obviously the same type (and will undergo no further conversions),
86 : // even when it's not strictly necessary, for explicitness.
87 :
88 : enum UUComparison { FromIsBigger, FromIsNotBigger };
89 :
90 : // Unsigned-to-unsigned range check
91 :
92 : template<typename From, typename To,
93 : UUComparison = (sizeof(From) > sizeof(To))
94 : ? FromIsBigger
95 : : FromIsNotBigger>
96 : struct UnsignedUnsignedCheck;
97 :
98 : template<typename From, typename To>
99 : struct UnsignedUnsignedCheck<From, To, FromIsBigger>
100 : {
101 : public:
102 : static bool checkBounds(const From aFrom)
103 : {
104 : return aFrom <= From(To(-1));
105 : }
106 : };
107 :
108 : template<typename From, typename To>
109 : struct UnsignedUnsignedCheck<From, To, FromIsNotBigger>
110 : {
111 : public:
112 : static bool checkBounds(const From aFrom)
113 : {
114 : return true;
115 : }
116 : };
117 :
118 : template<typename From, typename To>
119 : struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned>
120 : {
121 : public:
122 : static bool checkBounds(const From aFrom)
123 : {
124 137675 : return UnsignedUnsignedCheck<From, To>::checkBounds(aFrom);
125 : }
126 : };
127 :
128 : // Signed-to-unsigned range check
129 :
130 : template<typename From, typename To>
131 : struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned>
132 : {
133 : public:
134 : static bool checkBounds(const From aFrom)
135 : {
136 753 : if (aFrom < 0) {
137 : return false;
138 : }
139 : if (sizeof(To) >= sizeof(From)) {
140 : return true;
141 : }
142 0 : return aFrom <= From(To(-1));
143 : }
144 : };
145 :
146 : // Unsigned-to-signed range check
147 :
148 : enum USComparison { FromIsSmaller, FromIsNotSmaller };
149 :
150 : template<typename From, typename To,
151 : USComparison = (sizeof(From) < sizeof(To))
152 : ? FromIsSmaller
153 : : FromIsNotSmaller>
154 : struct UnsignedSignedCheck;
155 :
156 : template<typename From, typename To>
157 : struct UnsignedSignedCheck<From, To, FromIsSmaller>
158 : {
159 : public:
160 : static bool checkBounds(const From aFrom)
161 : {
162 : return true;
163 : }
164 : };
165 :
166 : template<typename From, typename To>
167 : struct UnsignedSignedCheck<From, To, FromIsNotSmaller>
168 : {
169 : public:
170 : static bool checkBounds(const From aFrom)
171 : {
172 984 : const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
173 984 : return aFrom <= From(MaxValue);
174 : }
175 : };
176 :
177 : template<typename From, typename To>
178 : struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned>
179 : {
180 : public:
181 : static bool checkBounds(const From aFrom)
182 : {
183 984 : return UnsignedSignedCheck<From, To>::checkBounds(aFrom);
184 : }
185 : };
186 :
187 : // Signed-to-signed range check
188 :
189 : template<typename From, typename To>
190 : struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned>
191 : {
192 : public:
193 : static bool checkBounds(const From aFrom)
194 : {
195 : if (sizeof(From) <= sizeof(To)) {
196 : return true;
197 : }
198 : const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
199 : const To MinValue = -MaxValue - To(1);
200 : return From(MinValue) <= aFrom &&
201 : From(aFrom) <= From(MaxValue);
202 : }
203 : };
204 :
205 : template<typename From, typename To,
206 : bool TypesAreIntegral = IsIntegral<From>::value &&
207 : IsIntegral<To>::value>
208 : class BoundsChecker;
209 :
210 : template<typename From>
211 : class BoundsChecker<From, From, true>
212 : {
213 : public:
214 : static bool checkBounds(const From aFrom) { return true; }
215 : };
216 :
217 : template<typename From, typename To>
218 : class BoundsChecker<From, To, true>
219 : {
220 : public:
221 : static bool checkBounds(const From aFrom)
222 : {
223 0 : return BoundsCheckImpl<From, To>::checkBounds(aFrom);
224 : }
225 : };
226 :
227 : template<typename From, typename To>
228 : inline bool
229 : IsInBounds(const From aFrom)
230 : {
231 139413 : return BoundsChecker<From, To>::checkBounds(aFrom);
232 : }
233 :
234 : } // namespace detail
235 :
236 : /**
237 : * Cast a value of integral type |From| to a value of integral type |To|,
238 : * asserting that the cast will be a safe cast per C++ (that is, that |to| is in
239 : * the range of values permitted for the type |From|).
240 : */
241 : template<typename To, typename From>
242 : inline To
243 : AssertedCast(const From aFrom)
244 : {
245 : MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
246 : return static_cast<To>(aFrom);
247 : }
248 :
249 : /**
250 : * Cast a value of integral type |From| to a value of integral type |To|,
251 : * release asserting that the cast will be a safe cast per C++ (that is, that
252 : * |to| is in the range of values permitted for the type |From|).
253 : */
254 : template<typename To, typename From>
255 : inline To
256 : ReleaseAssertedCast(const From aFrom)
257 : {
258 : MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom)));
259 : return static_cast<To>(aFrom);
260 : }
261 :
262 : } // namespace mozilla
263 :
264 : #endif /* mozilla_Casting_h */
|