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/. */
7 /* Cast operations to supplement the built-in casting operations. */
9 #ifndef mozilla_Casting_h
10 #define mozilla_Casting_h
12 #include "mozilla/Assertions.h"
13 #include "mozilla/TypeTraits.h"
17 #include <type_traits>
22 * Sets the outparam value of type |To| with the same underlying bit pattern of
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
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
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.
42 template<typename To
, typename From
>
44 BitwiseCast(const From aFrom
, To
* aResult
)
46 static_assert(sizeof(From
) == sizeof(To
),
47 "To and From must have the same size");
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 "
54 static_assert(std::is_trivial
<To
>::value
,
55 "shouldn't bitwise-copy a type having non-trivial "
58 std::memcpy(static_cast<void*>(aResult
),
59 static_cast<const void*>(&aFrom
),
63 template<typename To
, typename From
>
65 BitwiseCast(const From aFrom
)
68 BitwiseCast
<To
, From
>(aFrom
, &temp
);
74 enum ToSignedness
{ ToIsSigned
, ToIsUnsigned
};
75 enum FromSignedness
{ FromIsSigned
, FromIsUnsigned
};
77 template<typename From
,
79 FromSignedness
= IsSigned
<From
>::value
? FromIsSigned
: FromIsUnsigned
,
80 ToSignedness
= IsSigned
<To
>::value
? ToIsSigned
: ToIsUnsigned
>
81 struct BoundsCheckImpl
;
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.
88 enum UUComparison
{ FromIsBigger
, FromIsNotBigger
};
90 // Unsigned-to-unsigned range check
92 template<typename From
, typename To
,
93 UUComparison
= (sizeof(From
) > sizeof(To
))
96 struct UnsignedUnsignedCheck
;
98 template<typename From
, typename To
>
99 struct UnsignedUnsignedCheck
<From
, To
, FromIsBigger
>
102 static bool checkBounds(const From aFrom
)
104 return aFrom
<= From(To(-1));
108 template<typename From
, typename To
>
109 struct UnsignedUnsignedCheck
<From
, To
, FromIsNotBigger
>
112 static bool checkBounds(const From aFrom
)
118 template<typename From
, typename To
>
119 struct BoundsCheckImpl
<From
, To
, FromIsUnsigned
, ToIsUnsigned
>
122 static bool checkBounds(const From aFrom
)
124 return UnsignedUnsignedCheck
<From
, To
>::checkBounds(aFrom
);
128 // Signed-to-unsigned range check
130 template<typename From
, typename To
>
131 struct BoundsCheckImpl
<From
, To
, FromIsSigned
, ToIsUnsigned
>
134 static bool checkBounds(const From aFrom
)
139 if (sizeof(To
) >= sizeof(From
)) {
142 return aFrom
<= From(To(-1));
146 // Unsigned-to-signed range check
148 enum USComparison
{ FromIsSmaller
, FromIsNotSmaller
};
150 template<typename From
, typename To
,
151 USComparison
= (sizeof(From
) < sizeof(To
))
154 struct UnsignedSignedCheck
;
156 template<typename From
, typename To
>
157 struct UnsignedSignedCheck
<From
, To
, FromIsSmaller
>
160 static bool checkBounds(const From aFrom
)
166 template<typename From
, typename To
>
167 struct UnsignedSignedCheck
<From
, To
, FromIsNotSmaller
>
170 static bool checkBounds(const From aFrom
)
172 const To MaxValue
= To((1ULL << (CHAR_BIT
* sizeof(To
) - 1)) - 1);
173 return aFrom
<= From(MaxValue
);
177 template<typename From
, typename To
>
178 struct BoundsCheckImpl
<From
, To
, FromIsUnsigned
, ToIsSigned
>
181 static bool checkBounds(const From aFrom
)
183 return UnsignedSignedCheck
<From
, To
>::checkBounds(aFrom
);
187 // Signed-to-signed range check
189 template<typename From
, typename To
>
190 struct BoundsCheckImpl
<From
, To
, FromIsSigned
, ToIsSigned
>
193 static bool checkBounds(const From aFrom
)
195 if (sizeof(From
) <= sizeof(To
)) {
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
);
205 template<typename From
, typename To
,
206 bool TypesAreIntegral
= IsIntegral
<From
>::value
&&
207 IsIntegral
<To
>::value
>
210 template<typename From
>
211 class BoundsChecker
<From
, From
, true>
214 static bool checkBounds(const From aFrom
) { return true; }
217 template<typename From
, typename To
>
218 class BoundsChecker
<From
, To
, true>
221 static bool checkBounds(const From aFrom
)
223 return BoundsCheckImpl
<From
, To
>::checkBounds(aFrom
);
227 template<typename From
, typename To
>
229 IsInBounds(const From aFrom
)
231 return BoundsChecker
<From
, To
>::checkBounds(aFrom
);
234 } // namespace detail
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|).
241 template<typename To
, typename From
>
243 AssertedCast(const From aFrom
)
245 MOZ_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
246 return static_cast<To
>(aFrom
);
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|).
254 template<typename To
, typename From
>
256 ReleaseAssertedCast(const From aFrom
)
258 MOZ_RELEASE_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
259 return static_cast<To
>(aFrom
);
262 } // namespace mozilla
264 #endif /* mozilla_Casting_h */