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 <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>
41 : inline void
42 177662 : BitwiseCast(const From aFrom, To* aResult)
43 : {
44 : static_assert(sizeof(From) == sizeof(To),
45 : "To and From must have the same size");
46 : union
47 0 : {
48 : From mFrom;
49 : To mTo;
50 0 : } u;
51 177662 : u.mFrom = aFrom;
52 177662 : *aResult = u.mTo;
53 177662 : }
54 :
55 : template<typename To, typename From>
56 : inline To
57 177430 : BitwiseCast(const From aFrom)
58 : {
59 0 : To temp;
60 177430 : BitwiseCast<To, From>(aFrom, &temp);
61 177430 : return temp;
62 : }
63 :
64 : namespace detail {
65 :
66 : enum ToSignedness { ToIsSigned, ToIsUnsigned };
67 : enum FromSignedness { FromIsSigned, FromIsUnsigned };
68 :
69 : template<typename From,
70 : typename To,
71 : FromSignedness = IsSigned<From>::value ? FromIsSigned : FromIsUnsigned,
72 : ToSignedness = IsSigned<To>::value ? ToIsSigned : ToIsUnsigned>
73 : struct BoundsCheckImpl;
74 :
75 : // Implicit conversions on operands to binary operations make this all a bit
76 : // hard to verify. Attempt to ease the pain below by *only* comparing values
77 : // that are obviously the same type (and will undergo no further conversions),
78 : // even when it's not strictly necessary, for explicitness.
79 :
80 : enum UUComparison { FromIsBigger, FromIsNotBigger };
81 :
82 : // Unsigned-to-unsigned range check
83 :
84 : template<typename From, typename To,
85 : UUComparison = (sizeof(From) > sizeof(To))
86 : ? FromIsBigger
87 : : FromIsNotBigger>
88 : struct UnsignedUnsignedCheck;
89 :
90 : template<typename From, typename To>
91 : struct UnsignedUnsignedCheck<From, To, FromIsBigger>
92 : {
93 : public:
94 78483 : static bool checkBounds(const From aFrom)
95 : {
96 78483 : return aFrom <= From(To(-1));
97 : }
98 : };
99 :
100 : template<typename From, typename To>
101 : struct UnsignedUnsignedCheck<From, To, FromIsNotBigger>
102 : {
103 : public:
104 : static bool checkBounds(const From aFrom)
105 : {
106 : return true;
107 : }
108 : };
109 :
110 : template<typename From, typename To>
111 : struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned>
112 : {
113 : public:
114 78483 : static bool checkBounds(const From aFrom)
115 : {
116 78483 : return UnsignedUnsignedCheck<From, To>::checkBounds(aFrom);
117 : }
118 : };
119 :
120 : // Signed-to-unsigned range check
121 :
122 : template<typename From, typename To>
123 : struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned>
124 : {
125 : public:
126 1035 : static bool checkBounds(const From aFrom)
127 : {
128 1035 : if (aFrom < 0) {
129 0 : return false;
130 : }
131 : if (sizeof(To) >= sizeof(From)) {
132 1035 : return true;
133 : }
134 0 : return aFrom <= From(To(-1));
135 : }
136 : };
137 :
138 : // Unsigned-to-signed range check
139 :
140 : enum USComparison { FromIsSmaller, FromIsNotSmaller };
141 :
142 : template<typename From, typename To,
143 : USComparison = (sizeof(From) < sizeof(To))
144 : ? FromIsSmaller
145 : : FromIsNotSmaller>
146 : struct UnsignedSignedCheck;
147 :
148 : template<typename From, typename To>
149 : struct UnsignedSignedCheck<From, To, FromIsSmaller>
150 : {
151 : public:
152 : static bool checkBounds(const From aFrom)
153 : {
154 : return true;
155 : }
156 : };
157 :
158 : template<typename From, typename To>
159 : struct UnsignedSignedCheck<From, To, FromIsNotSmaller>
160 : {
161 : public:
162 478 : static bool checkBounds(const From aFrom)
163 : {
164 478 : const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
165 478 : return aFrom <= From(MaxValue);
166 : }
167 : };
168 :
169 : template<typename From, typename To>
170 : struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned>
171 : {
172 : public:
173 478 : static bool checkBounds(const From aFrom)
174 : {
175 478 : return UnsignedSignedCheck<From, To>::checkBounds(aFrom);
176 : }
177 : };
178 :
179 : // Signed-to-signed range check
180 :
181 : template<typename From, typename To>
182 : struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned>
183 : {
184 : public:
185 : static bool checkBounds(const From aFrom)
186 : {
187 : if (sizeof(From) <= sizeof(To)) {
188 : return true;
189 : }
190 : const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
191 : const To MinValue = -MaxValue - To(1);
192 : return From(MinValue) <= aFrom &&
193 : From(aFrom) <= From(MaxValue);
194 : }
195 : };
196 :
197 : template<typename From, typename To,
198 : bool TypesAreIntegral = IsIntegral<From>::value &&
199 : IsIntegral<To>::value>
200 : class BoundsChecker;
201 :
202 : template<typename From>
203 : class BoundsChecker<From, From, true>
204 : {
205 : public:
206 0 : static bool checkBounds(const From aFrom) { return true; }
207 : };
208 :
209 : template<typename From, typename To>
210 : class BoundsChecker<From, To, true>
211 : {
212 : public:
213 79997 : static bool checkBounds(const From aFrom)
214 : {
215 79997 : return BoundsCheckImpl<From, To>::checkBounds(aFrom);
216 : }
217 : };
218 :
219 : template<typename From, typename To>
220 : inline bool
221 79998 : IsInBounds(const From aFrom)
222 : {
223 79998 : return BoundsChecker<From, To>::checkBounds(aFrom);
224 : }
225 :
226 : } // namespace detail
227 :
228 : /**
229 : * Cast a value of integral type |From| to a value of integral type |To|,
230 : * asserting that the cast will be a safe cast per C++ (that is, that |to| is in
231 : * the range of values permitted for the type |From|).
232 : */
233 : template<typename To, typename From>
234 : inline To
235 79998 : AssertedCast(const From aFrom)
236 : {
237 79998 : MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
238 79998 : return static_cast<To>(aFrom);
239 : }
240 :
241 : /**
242 : * Cast a value of integral type |From| to a value of integral type |To|,
243 : * release asserting that the cast will be a safe cast per C++ (that is, that
244 : * |to| is in the range of values permitted for the type |From|).
245 : */
246 : template<typename To, typename From>
247 : inline To
248 0 : ReleaseAssertedCast(const From aFrom)
249 : {
250 0 : MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom)));
251 0 : return static_cast<To>(aFrom);
252 : }
253 :
254 : } // namespace mozilla
255 :
256 : #endif /* mozilla_Casting_h */
|