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"
16 #include <type_traits>
21 * Sets the outparam value of type |To| with the same underlying bit pattern of
24 * |To| and |From| must be types of the same size; be careful of cross-platform
25 * size differences, or this might fail to compile on some but not all
28 * There is also a variant that returns the value directly. In most cases, the
29 * two variants should be identical. However, in the specific case of x86
30 * chips, the behavior differs: returning floating-point values directly is done
31 * through the x87 stack, and x87 loads and stores turn signaling NaNs into
32 * quiet NaNs... silently. Returning floating-point values via outparam,
33 * however, is done entirely within the SSE registers when SSE2 floating-point
34 * is enabled in the compiler, which has semantics-preserving behavior you would
37 * If preserving the distinction between signaling NaNs and quiet NaNs is
38 * important to you, you should use the outparam version. In all other cases,
39 * you should use the direct return version.
41 template <typename To
, typename From
>
42 inline void BitwiseCast(const From aFrom
, To
* aResult
) {
43 static_assert(sizeof(From
) == sizeof(To
),
44 "To and From must have the same size");
46 // We could maybe downgrade these to std::is_trivially_copyable, but the
47 // various STLs we use don't all provide it.
48 static_assert(std::is_trivial
<From
>::value
,
49 "shouldn't bitwise-copy a type having non-trivial "
51 static_assert(std::is_trivial
<To
>::value
,
52 "shouldn't bitwise-copy a type having non-trivial "
55 std::memcpy(static_cast<void*>(aResult
), static_cast<const void*>(&aFrom
),
59 template <typename To
, typename From
>
60 inline To
BitwiseCast(const From aFrom
) {
62 BitwiseCast
<To
, From
>(aFrom
, &temp
);
68 enum ToSignedness
{ ToIsSigned
, ToIsUnsigned
};
69 enum FromSignedness
{ FromIsSigned
, FromIsUnsigned
};
71 template <typename From
, typename To
,
73 std::is_signed_v
<From
> ? FromIsSigned
: FromIsUnsigned
,
74 ToSignedness
= std::is_signed_v
<To
> ? ToIsSigned
: ToIsUnsigned
>
75 struct BoundsCheckImpl
;
77 // Implicit conversions on operands to binary operations make this all a bit
78 // hard to verify. Attempt to ease the pain below by *only* comparing values
79 // that are obviously the same type (and will undergo no further conversions),
80 // even when it's not strictly necessary, for explicitness.
82 enum UUComparison
{ FromIsBigger
, FromIsNotBigger
};
84 // Unsigned-to-unsigned range check
86 template <typename From
, typename To
,
88 (sizeof(From
) > sizeof(To
)) ? FromIsBigger
: FromIsNotBigger
>
89 struct UnsignedUnsignedCheck
;
91 template <typename From
, typename To
>
92 struct UnsignedUnsignedCheck
<From
, To
, FromIsBigger
> {
94 static bool checkBounds(const From aFrom
) { return aFrom
<= From(To(-1)); }
97 template <typename From
, typename To
>
98 struct UnsignedUnsignedCheck
<From
, To
, FromIsNotBigger
> {
100 static bool checkBounds(const From aFrom
) { return true; }
103 template <typename From
, typename To
>
104 struct BoundsCheckImpl
<From
, To
, FromIsUnsigned
, ToIsUnsigned
> {
106 static bool checkBounds(const From aFrom
) {
107 return UnsignedUnsignedCheck
<From
, To
>::checkBounds(aFrom
);
111 // Signed-to-unsigned range check
113 template <typename From
, typename To
>
114 struct BoundsCheckImpl
<From
, To
, FromIsSigned
, ToIsUnsigned
> {
116 static bool checkBounds(const From aFrom
) {
120 if (sizeof(To
) >= sizeof(From
)) {
123 return aFrom
<= From(To(-1));
127 // Unsigned-to-signed range check
129 enum USComparison
{ FromIsSmaller
, FromIsNotSmaller
};
131 template <typename From
, typename To
,
133 (sizeof(From
) < sizeof(To
)) ? FromIsSmaller
: FromIsNotSmaller
>
134 struct UnsignedSignedCheck
;
136 template <typename From
, typename To
>
137 struct UnsignedSignedCheck
<From
, To
, FromIsSmaller
> {
139 static bool checkBounds(const From aFrom
) { return true; }
142 template <typename From
, typename To
>
143 struct UnsignedSignedCheck
<From
, To
, FromIsNotSmaller
> {
145 static bool checkBounds(const From aFrom
) {
146 const To MaxValue
= To((1ULL << (CHAR_BIT
* sizeof(To
) - 1)) - 1);
147 return aFrom
<= From(MaxValue
);
151 template <typename From
, typename To
>
152 struct BoundsCheckImpl
<From
, To
, FromIsUnsigned
, ToIsSigned
> {
154 static bool checkBounds(const From aFrom
) {
155 return UnsignedSignedCheck
<From
, To
>::checkBounds(aFrom
);
159 // Signed-to-signed range check
161 template <typename From
, typename To
>
162 struct BoundsCheckImpl
<From
, To
, FromIsSigned
, ToIsSigned
> {
164 static bool checkBounds(const From aFrom
) {
165 if (sizeof(From
) <= sizeof(To
)) {
168 const To MaxValue
= To((1ULL << (CHAR_BIT
* sizeof(To
) - 1)) - 1);
169 const To MinValue
= -MaxValue
- To(1);
170 return From(MinValue
) <= aFrom
&& From(aFrom
) <= From(MaxValue
);
174 template <typename From
, typename To
,
175 bool TypesAreIntegral
=
176 std::is_integral_v
<From
>&& std::is_integral_v
<To
>>
179 template <typename From
>
180 class BoundsChecker
<From
, From
, true> {
182 static bool checkBounds(const From aFrom
) { return true; }
185 template <typename From
, typename To
>
186 class BoundsChecker
<From
, To
, true> {
188 static bool checkBounds(const From aFrom
) {
189 return BoundsCheckImpl
<From
, To
>::checkBounds(aFrom
);
193 template <typename From
, typename To
>
194 inline bool IsInBounds(const From aFrom
) {
195 return BoundsChecker
<From
, To
>::checkBounds(aFrom
);
198 } // namespace detail
201 * Cast a value of integral type |From| to a value of integral type |To|,
202 * asserting that the cast will be a safe cast per C++ (that is, that |to| is in
203 * the range of values permitted for the type |From|).
205 template <typename To
, typename From
>
206 inline To
AssertedCast(const From aFrom
) {
207 MOZ_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
208 return static_cast<To
>(aFrom
);
212 * Cast a value of integral type |From| to a value of integral type |To|,
213 * release asserting that the cast will be a safe cast per C++ (that is, that
214 * |to| is in the range of values permitted for the type |From|).
216 template <typename To
, typename From
>
217 inline To
ReleaseAssertedCast(const From aFrom
) {
218 MOZ_RELEASE_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
219 return static_cast<To
>(aFrom
);
222 } // namespace mozilla
224 #endif /* mozilla_Casting_h */