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
>
43 inline void BitwiseCast(const From aFrom
, To
* aResult
) {
44 static_assert(sizeof(From
) == sizeof(To
),
45 "To and From must have the same size");
47 // We could maybe downgrade these to std::is_trivially_copyable, but the
48 // various STLs we use don't all provide it.
49 static_assert(std::is_trivial
<From
>::value
,
50 "shouldn't bitwise-copy a type having non-trivial "
52 static_assert(std::is_trivial
<To
>::value
,
53 "shouldn't bitwise-copy a type having non-trivial "
56 std::memcpy(static_cast<void*>(aResult
), static_cast<const void*>(&aFrom
),
60 template <typename To
, typename From
>
61 inline To
BitwiseCast(const From aFrom
) {
63 BitwiseCast
<To
, From
>(aFrom
, &temp
);
69 enum ToSignedness
{ ToIsSigned
, ToIsUnsigned
};
70 enum FromSignedness
{ FromIsSigned
, FromIsUnsigned
};
72 template <typename From
, typename To
,
74 IsSigned
<From
>::value
? FromIsSigned
: FromIsUnsigned
,
75 ToSignedness
= IsSigned
<To
>::value
? ToIsSigned
: ToIsUnsigned
>
76 struct BoundsCheckImpl
;
78 // Implicit conversions on operands to binary operations make this all a bit
79 // hard to verify. Attempt to ease the pain below by *only* comparing values
80 // that are obviously the same type (and will undergo no further conversions),
81 // even when it's not strictly necessary, for explicitness.
83 enum UUComparison
{ FromIsBigger
, FromIsNotBigger
};
85 // Unsigned-to-unsigned range check
87 template <typename From
, typename To
,
89 (sizeof(From
) > sizeof(To
)) ? FromIsBigger
: FromIsNotBigger
>
90 struct UnsignedUnsignedCheck
;
92 template <typename From
, typename To
>
93 struct UnsignedUnsignedCheck
<From
, To
, FromIsBigger
> {
95 static bool checkBounds(const From aFrom
) { return aFrom
<= From(To(-1)); }
98 template <typename From
, typename To
>
99 struct UnsignedUnsignedCheck
<From
, To
, FromIsNotBigger
> {
101 static bool checkBounds(const From aFrom
) { return true; }
104 template <typename From
, typename To
>
105 struct BoundsCheckImpl
<From
, To
, FromIsUnsigned
, ToIsUnsigned
> {
107 static bool checkBounds(const From aFrom
) {
108 return UnsignedUnsignedCheck
<From
, To
>::checkBounds(aFrom
);
112 // Signed-to-unsigned range check
114 template <typename From
, typename To
>
115 struct BoundsCheckImpl
<From
, To
, FromIsSigned
, ToIsUnsigned
> {
117 static bool checkBounds(const From aFrom
) {
121 if (sizeof(To
) >= sizeof(From
)) {
124 return aFrom
<= From(To(-1));
128 // Unsigned-to-signed range check
130 enum USComparison
{ FromIsSmaller
, FromIsNotSmaller
};
132 template <typename From
, typename To
,
134 (sizeof(From
) < sizeof(To
)) ? FromIsSmaller
: FromIsNotSmaller
>
135 struct UnsignedSignedCheck
;
137 template <typename From
, typename To
>
138 struct UnsignedSignedCheck
<From
, To
, FromIsSmaller
> {
140 static bool checkBounds(const From aFrom
) { return true; }
143 template <typename From
, typename To
>
144 struct UnsignedSignedCheck
<From
, To
, FromIsNotSmaller
> {
146 static bool checkBounds(const From aFrom
) {
147 const To MaxValue
= To((1ULL << (CHAR_BIT
* sizeof(To
) - 1)) - 1);
148 return aFrom
<= From(MaxValue
);
152 template <typename From
, typename To
>
153 struct BoundsCheckImpl
<From
, To
, FromIsUnsigned
, ToIsSigned
> {
155 static bool checkBounds(const From aFrom
) {
156 return UnsignedSignedCheck
<From
, To
>::checkBounds(aFrom
);
160 // Signed-to-signed range check
162 template <typename From
, typename To
>
163 struct BoundsCheckImpl
<From
, To
, FromIsSigned
, ToIsSigned
> {
165 static bool checkBounds(const From aFrom
) {
166 if (sizeof(From
) <= sizeof(To
)) {
169 const To MaxValue
= To((1ULL << (CHAR_BIT
* sizeof(To
) - 1)) - 1);
170 const To MinValue
= -MaxValue
- To(1);
171 return From(MinValue
) <= aFrom
&& From(aFrom
) <= From(MaxValue
);
175 template <typename From
, typename To
,
176 bool TypesAreIntegral
=
177 IsIntegral
<From
>::value
&& IsIntegral
<To
>::value
>
180 template <typename From
>
181 class BoundsChecker
<From
, From
, true> {
183 static bool checkBounds(const From aFrom
) { return true; }
186 template <typename From
, typename To
>
187 class BoundsChecker
<From
, To
, true> {
189 static bool checkBounds(const From aFrom
) {
190 return BoundsCheckImpl
<From
, To
>::checkBounds(aFrom
);
194 template <typename From
, typename To
>
195 inline bool IsInBounds(const From aFrom
) {
196 return BoundsChecker
<From
, To
>::checkBounds(aFrom
);
199 } // namespace detail
202 * Cast a value of integral type |From| to a value of integral type |To|,
203 * asserting that the cast will be a safe cast per C++ (that is, that |to| is in
204 * the range of values permitted for the type |From|).
206 template <typename To
, typename From
>
207 inline To
AssertedCast(const From aFrom
) {
208 MOZ_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
209 return static_cast<To
>(aFrom
);
213 * Cast a value of integral type |From| to a value of integral type |To|,
214 * release asserting that the cast will be a safe cast per C++ (that is, that
215 * |to| is in the range of values permitted for the type |From|).
217 template <typename To
, typename From
>
218 inline To
ReleaseAssertedCast(const From aFrom
) {
219 MOZ_RELEASE_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
220 return static_cast<To
>(aFrom
);
223 } // namespace mozilla
225 #endif /* mozilla_Casting_h */