Bug 1806067 [wpt PR 37543] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / mfbt / Casting.h
blobbd8b03f92edd2236ef54a7f2b6d256905bd1001c
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"
14 #include <cstring>
15 #include <limits.h>
16 #include <type_traits>
18 namespace mozilla {
20 /**
21 * Sets the outparam value of type |To| with the same underlying bit pattern of
22 * |aFrom|.
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
26 * platforms.
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
35 * expect.
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 "
50 "initialization");
51 static_assert(std::is_trivial<To>::value,
52 "shouldn't bitwise-copy a type having non-trivial "
53 "initialization");
55 std::memcpy(static_cast<void*>(aResult), static_cast<const void*>(&aFrom),
56 sizeof(From));
59 template <typename To, typename From>
60 inline To BitwiseCast(const From aFrom) {
61 To temp;
62 BitwiseCast<To, From>(aFrom, &temp);
63 return temp;
66 namespace detail {
68 enum ToSignedness { ToIsSigned, ToIsUnsigned };
69 enum FromSignedness { FromIsSigned, FromIsUnsigned };
71 template <typename From, typename To,
72 FromSignedness =
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,
87 UUComparison =
88 (sizeof(From) > sizeof(To)) ? FromIsBigger : FromIsNotBigger>
89 struct UnsignedUnsignedCheck;
91 template <typename From, typename To>
92 struct UnsignedUnsignedCheck<From, To, FromIsBigger> {
93 public:
94 static bool checkBounds(const From aFrom) { return aFrom <= From(To(-1)); }
97 template <typename From, typename To>
98 struct UnsignedUnsignedCheck<From, To, FromIsNotBigger> {
99 public:
100 static bool checkBounds(const From aFrom) { return true; }
103 template <typename From, typename To>
104 struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned> {
105 public:
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> {
115 public:
116 static bool checkBounds(const From aFrom) {
117 if (aFrom < 0) {
118 return false;
120 if (sizeof(To) >= sizeof(From)) {
121 return true;
123 return aFrom <= From(To(-1));
127 // Unsigned-to-signed range check
129 enum USComparison { FromIsSmaller, FromIsNotSmaller };
131 template <typename From, typename To,
132 USComparison =
133 (sizeof(From) < sizeof(To)) ? FromIsSmaller : FromIsNotSmaller>
134 struct UnsignedSignedCheck;
136 template <typename From, typename To>
137 struct UnsignedSignedCheck<From, To, FromIsSmaller> {
138 public:
139 static bool checkBounds(const From aFrom) { return true; }
142 template <typename From, typename To>
143 struct UnsignedSignedCheck<From, To, FromIsNotSmaller> {
144 public:
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> {
153 public:
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> {
163 public:
164 static bool checkBounds(const From aFrom) {
165 if (sizeof(From) <= sizeof(To)) {
166 return true;
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>>
177 class BoundsChecker;
179 template <typename From>
180 class BoundsChecker<From, From, true> {
181 public:
182 static bool checkBounds(const From aFrom) { return true; }
185 template <typename From, typename To>
186 class BoundsChecker<From, To, true> {
187 public:
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 */