Bug 1812353 - Update GPUSupportedLimits in webgpu.webidl. r=webgpu-reviewers,webidl...
[gecko.git] / mfbt / Casting.h
blobebb0e8bc512bf9b37048d4ef3975e6c74a636cf7
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 <type_traits>
16 #include <limits>
17 #include <cmath>
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 template <typename T>
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);
75 template <typename T>
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,
82 // fixed in gcc-10
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)) {
103 return false;
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
111 // output type.
112 if (aIn < static_cast<double>(outMin) ||
113 aIn > static_cast<double>(outMax)) {
114 return false;
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.
118 if (outSigned) {
119 int64_t asInteger = static_cast<int64_t>(aIn);
120 if (asInteger < outMin || asInteger > outMax) {
121 return false;
123 } else {
124 uint64_t asInteger = static_cast<uint64_t>(aIn);
125 if (asInteger > outMax) {
126 return false;
131 // Checks if the integer is representable exactly as a floating point value of
132 // a specific width.
133 if constexpr (!inFloat && outFloat) {
134 if constexpr (inSigned) {
135 if (aIn < -safe_integer<Out>() || aIn > safe_integer<Out>()) {
136 return false;
138 } else {
139 if (aIn >= safe_integer_unsigned<Out>()) {
140 return false;
145 if constexpr (noneFloat) {
146 if constexpr (bothUnsigned) {
147 if (aIn > select_widest(outMax)) {
148 return false;
151 if constexpr (bothSigned) {
152 if (aIn > select_widest(outMax) || aIn < select_widest(outMin)) {
153 return false;
156 if constexpr (inSigned && !outSigned) {
157 if (aIn < 0 || std::make_unsigned_t<In>(aIn) > outMax) {
158 return false;
161 if constexpr (!inSigned && outSigned) {
162 if (aIn > select_widest(outMax)) {
163 return false;
167 return true;
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);
201 namespace detail {
203 template <typename From>
204 class LazyAssertedCastT final {
205 const From mVal;
207 public:
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 */