Bug 1590484 [wpt PR 19830] - Fix flakiness of web-animation/interfaces/Animation...
[gecko.git] / mfbt / Casting.h
bloba27adfec0668efaf2f889560409fa104e261d08b
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"
15 #include <cstring>
16 #include <limits.h>
17 #include <type_traits>
19 namespace mozilla {
21 /**
22 * Sets the outparam value of type |To| with the same underlying bit pattern of
23 * |aFrom|.
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
27 * platforms.
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
36 * expect.
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 "
51 "initialization");
52 static_assert(std::is_trivial<To>::value,
53 "shouldn't bitwise-copy a type having non-trivial "
54 "initialization");
56 std::memcpy(static_cast<void*>(aResult), static_cast<const void*>(&aFrom),
57 sizeof(From));
60 template <typename To, typename From>
61 inline To BitwiseCast(const From aFrom) {
62 To temp;
63 BitwiseCast<To, From>(aFrom, &temp);
64 return temp;
67 namespace detail {
69 enum ToSignedness { ToIsSigned, ToIsUnsigned };
70 enum FromSignedness { FromIsSigned, FromIsUnsigned };
72 template <typename From, typename To,
73 FromSignedness =
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,
88 UUComparison =
89 (sizeof(From) > sizeof(To)) ? FromIsBigger : FromIsNotBigger>
90 struct UnsignedUnsignedCheck;
92 template <typename From, typename To>
93 struct UnsignedUnsignedCheck<From, To, FromIsBigger> {
94 public:
95 static bool checkBounds(const From aFrom) { return aFrom <= From(To(-1)); }
98 template <typename From, typename To>
99 struct UnsignedUnsignedCheck<From, To, FromIsNotBigger> {
100 public:
101 static bool checkBounds(const From aFrom) { return true; }
104 template <typename From, typename To>
105 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned> {
106 public:
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> {
116 public:
117 static bool checkBounds(const From aFrom) {
118 if (aFrom < 0) {
119 return false;
121 if (sizeof(To) >= sizeof(From)) {
122 return true;
124 return aFrom <= From(To(-1));
128 // Unsigned-to-signed range check
130 enum USComparison { FromIsSmaller, FromIsNotSmaller };
132 template <typename From, typename To,
133 USComparison =
134 (sizeof(From) < sizeof(To)) ? FromIsSmaller : FromIsNotSmaller>
135 struct UnsignedSignedCheck;
137 template <typename From, typename To>
138 struct UnsignedSignedCheck<From, To, FromIsSmaller> {
139 public:
140 static bool checkBounds(const From aFrom) { return true; }
143 template <typename From, typename To>
144 struct UnsignedSignedCheck<From, To, FromIsNotSmaller> {
145 public:
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> {
154 public:
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> {
164 public:
165 static bool checkBounds(const From aFrom) {
166 if (sizeof(From) <= sizeof(To)) {
167 return true;
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>
178 class BoundsChecker;
180 template <typename From>
181 class BoundsChecker<From, From, true> {
182 public:
183 static bool checkBounds(const From aFrom) { return true; }
186 template <typename From, typename To>
187 class BoundsChecker<From, To, true> {
188 public:
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 */