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"
15 #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
);
70 constexpr int64_t safe_integer() {
71 static_assert(std::is_floating_point_v
<T
>);
72 return std::pow(2, std::numeric_limits
<T
>::digits
);
76 constexpr uint64_t safe_integer_unsigned() {
77 static_assert(std::is_floating_point_v
<T
>);
78 return std::pow(2, std::numeric_limits
<T
>::digits
);
81 // This is working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81676,
83 #pragma GCC diagnostic push
84 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
85 template <typename In
, typename Out
>
86 bool IsInBounds(In aIn
) {
87 constexpr bool inSigned
= std::is_signed_v
<In
>;
88 constexpr bool outSigned
= std::is_signed_v
<Out
>;
89 constexpr bool bothSigned
= inSigned
&& outSigned
;
90 constexpr bool bothUnsigned
= !inSigned
&& !outSigned
;
91 constexpr bool inFloat
= std::is_floating_point_v
<In
>;
92 constexpr bool outFloat
= std::is_floating_point_v
<Out
>;
93 constexpr bool bothFloat
= inFloat
&& outFloat
;
94 constexpr bool noneFloat
= !inFloat
&& !outFloat
;
95 constexpr Out outMax
= std::numeric_limits
<Out
>::max();
96 constexpr Out outMin
= std::numeric_limits
<Out
>::lowest();
98 // This selects the widest of two types, and is used to cast throughout.
99 using select_widest
= std::conditional_t
<(sizeof(In
) > sizeof(Out
)), In
, Out
>;
101 if constexpr (bothFloat
) {
102 if (aIn
> select_widest(outMax
) || aIn
< select_widest(outMin
)) {
106 // Normal casting applies, the floating point number is floored.
107 if constexpr (inFloat
&& !outFloat
) {
108 static_assert(sizeof(aIn
) <= sizeof(int64_t));
109 // Check if the input floating point is larger than the output bounds. This
110 // catches situations where the input is a float larger than the max of the
112 if (aIn
< static_cast<double>(outMin
) ||
113 aIn
> static_cast<double>(outMax
)) {
116 // At this point we know that the input can be converted to an integer.
117 // Check if it's larger than the bounds of the target integer.
119 int64_t asInteger
= static_cast<int64_t>(aIn
);
120 if (asInteger
< outMin
|| asInteger
> outMax
) {
124 uint64_t asInteger
= static_cast<uint64_t>(aIn
);
125 if (asInteger
> outMax
) {
131 // Checks if the integer is representable exactly as a floating point value of
133 if constexpr (!inFloat
&& outFloat
) {
134 if constexpr (inSigned
) {
135 if (aIn
< -safe_integer
<Out
>() || aIn
> safe_integer
<Out
>()) {
139 if (aIn
>= safe_integer_unsigned
<Out
>()) {
145 if constexpr (noneFloat
) {
146 if constexpr (bothUnsigned
) {
147 if (aIn
> select_widest(outMax
)) {
151 if constexpr (bothSigned
) {
152 if (aIn
> select_widest(outMax
) || aIn
< select_widest(outMin
)) {
156 if constexpr (inSigned
&& !outSigned
) {
157 if (aIn
< 0 || std::make_unsigned_t
<In
>(aIn
) > outMax
) {
161 if constexpr (!inSigned
&& outSigned
) {
162 if (aIn
> select_widest(outMax
)) {
169 #pragma GCC diagnostic pop
171 } // namespace detail
174 * Cast a value of type |From| to a value of type |To|, asserting that the cast
175 * will be a safe cast per C++ (that is, that |to| is in the range of values
176 * permitted for the type |From|).
177 * In particular, this will fail if a integer cannot be represented exactly as a
178 * floating point value, because it's too large.
180 template <typename To
, typename From
>
181 inline To
AssertedCast(const From aFrom
) {
182 static_assert(std::is_arithmetic_v
<To
> && std::is_arithmetic_v
<From
>);
183 MOZ_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
184 return static_cast<To
>(aFrom
);
188 * Cast a value of numeric type |From| to a value of numeric type |To|, release
189 * asserting that the cast will be a safe cast per C++ (that is, that |to| is in
190 * the range of values permitted for the type |From|).
191 * In particular, this will fail if a integer cannot be represented exactly as a
192 * floating point value, because it's too large.
194 template <typename To
, typename From
>
195 inline To
ReleaseAssertedCast(const From aFrom
) {
196 static_assert(std::is_arithmetic_v
<To
> && std::is_arithmetic_v
<From
>);
197 MOZ_RELEASE_ASSERT((detail::IsInBounds
<From
, To
>(aFrom
)));
198 return static_cast<To
>(aFrom
);
203 template <typename From
>
204 class LazyAssertedCastT final
{
208 explicit LazyAssertedCastT(const From val
) : mVal(val
) {}
210 template <typename To
>
211 operator To() const {
212 return AssertedCast
<To
>(mVal
);
216 } // namespace detail
219 * Like AssertedCast, but infers |To| for AssertedCast lazily based on usage.
220 * > uint8_t foo = LazyAssertedCast(1000); // boom
222 template <typename From
>
223 inline auto LazyAssertedCast(const From val
) {
224 return detail::LazyAssertedCastT
<From
>(val
);
227 } // namespace mozilla
229 #endif /* mozilla_Casting_h */