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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Casting.h"
8 #include "mozilla/ThreadSafety.h"
13 #include <type_traits>
15 using mozilla::AssertedCast
;
16 using mozilla::BitwiseCast
;
17 using mozilla::detail::IsInBounds
;
19 static const uint8_t floatMantissaBitsPlusOne
= 24;
20 static const uint8_t doubleMantissaBitsPlusOne
= 53;
22 template <typename Uint
, typename Ulong
, bool = (sizeof(Uint
) == sizeof(Ulong
))>
23 struct UintUlongBitwiseCast
;
25 template <typename Uint
, typename Ulong
>
26 struct UintUlongBitwiseCast
<Uint
, Ulong
, true> {
28 MOZ_RELEASE_ASSERT(BitwiseCast
<Ulong
>(Uint(8675309)) == Ulong(8675309));
32 template <typename Uint
, typename Ulong
>
33 struct UintUlongBitwiseCast
<Uint
, Ulong
, false> {
37 static void TestBitwiseCast() {
38 MOZ_RELEASE_ASSERT(BitwiseCast
<int>(int(8675309)) == int(8675309));
39 UintUlongBitwiseCast
<unsigned int, unsigned long>::test();
42 static void TestSameSize() {
43 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, int16_t>(int16_t(0))));
44 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, int16_t>(int16_t(INT16_MIN
))));
45 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, int16_t>(int16_t(INT16_MAX
))));
46 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, uint16_t>(uint16_t(UINT16_MAX
))));
47 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, int16_t>(uint16_t(0))));
48 MOZ_RELEASE_ASSERT((!IsInBounds
<uint16_t, int16_t>(uint16_t(-1))));
49 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, uint16_t>(int16_t(-1))));
50 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, uint16_t>(int16_t(INT16_MAX
))));
51 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, uint16_t>(int16_t(INT16_MIN
))));
52 MOZ_RELEASE_ASSERT((IsInBounds
<int32_t, uint32_t>(int32_t(INT32_MAX
))));
53 MOZ_RELEASE_ASSERT((!IsInBounds
<int32_t, uint32_t>(int32_t(INT32_MIN
))));
56 static void TestToBiggerSize() {
57 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, int32_t>(int16_t(0))));
58 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, int32_t>(int16_t(INT16_MIN
))));
59 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, int32_t>(int16_t(INT16_MAX
))));
60 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, uint32_t>(uint16_t(UINT16_MAX
))));
61 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, int32_t>(uint16_t(0))));
62 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, int32_t>(uint16_t(-1))));
63 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, uint32_t>(int16_t(-1))));
64 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, uint32_t>(int16_t(INT16_MAX
))));
65 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, uint32_t>(int16_t(INT16_MIN
))));
66 MOZ_RELEASE_ASSERT((IsInBounds
<int32_t, uint64_t>(int32_t(INT32_MAX
))));
67 MOZ_RELEASE_ASSERT((!IsInBounds
<int32_t, uint64_t>(int32_t(INT32_MIN
))));
70 static void TestToSmallerSize() {
71 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, int8_t>(int16_t(0))));
72 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, int8_t>(int16_t(INT16_MIN
))));
73 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, int8_t>(int16_t(INT16_MAX
))));
74 MOZ_RELEASE_ASSERT((!IsInBounds
<uint16_t, uint8_t>(uint16_t(UINT16_MAX
))));
75 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, int8_t>(uint16_t(0))));
76 MOZ_RELEASE_ASSERT((!IsInBounds
<uint16_t, int8_t>(uint16_t(-1))));
77 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, uint8_t>(int16_t(-1))));
78 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, uint8_t>(int16_t(INT16_MAX
))));
79 MOZ_RELEASE_ASSERT((!IsInBounds
<int16_t, uint8_t>(int16_t(INT16_MIN
))));
80 MOZ_RELEASE_ASSERT((!IsInBounds
<int32_t, uint16_t>(int32_t(INT32_MAX
))));
81 MOZ_RELEASE_ASSERT((!IsInBounds
<int32_t, uint16_t>(int32_t(INT32_MIN
))));
84 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, int32_t>(int64_t(INT32_MIN
) - 1)));
85 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, int32_t>(int64_t(INT32_MIN
))));
86 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, int32_t>(int64_t(INT32_MIN
) + 1)));
87 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, int32_t>(int64_t(INT32_MAX
) - 1)));
88 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, int32_t>(int64_t(INT32_MAX
))));
89 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, int32_t>(int64_t(INT32_MAX
) + 1)));
91 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, uint32_t>(int64_t(-1))));
92 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, uint32_t>(int64_t(0))));
93 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, uint32_t>(int64_t(1))));
94 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, uint32_t>(int64_t(UINT32_MAX
) - 1)));
95 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, uint32_t>(int64_t(UINT32_MAX
))));
96 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, uint32_t>(int64_t(UINT32_MAX
) + 1)));
99 template <typename In
, typename Out
>
100 void checkBoundariesFloating(In aEpsilon
= {}, Out aIntegerOffset
= {}) {
101 // Check the max value of the input float can't be represented as an integer.
102 // This is true for all floating point and integer width.
103 MOZ_RELEASE_ASSERT((!IsInBounds
<In
, Out
>(std::numeric_limits
<In
>::max())));
104 // Check that the max value of the integer, as a float, minus an offset that
105 // depends on the magnitude, can be represented as an integer.
106 MOZ_RELEASE_ASSERT((IsInBounds
<In
, Out
>(
107 static_cast<In
>(std::numeric_limits
<Out
>::max() - aIntegerOffset
))));
108 // Check that the max value of the integer, plus a number that depends on the
109 // magnitude of the number, can't be represented as this integer (because it
111 MOZ_RELEASE_ASSERT((!IsInBounds
<In
, Out
>(
112 aEpsilon
+ static_cast<In
>(std::numeric_limits
<Out
>::max()))));
113 if constexpr (std::is_signed_v
<In
>) {
114 // Same for negative numbers.
116 (!IsInBounds
<In
, Out
>(std::numeric_limits
<In
>::lowest())));
117 MOZ_RELEASE_ASSERT((IsInBounds
<In
, Out
>(
118 static_cast<In
>(std::numeric_limits
<Out
>::lowest()))));
119 MOZ_RELEASE_ASSERT((!IsInBounds
<In
, Out
>(
120 static_cast<In
>(std::numeric_limits
<Out
>::lowest()) - aEpsilon
)));
122 // Check for negative floats and unsigned integer types.
123 MOZ_RELEASE_ASSERT((!IsInBounds
<In
, Out
>(static_cast<In
>(-1))));
127 void TestFloatConversion() {
128 MOZ_RELEASE_ASSERT((!IsInBounds
<uint64_t, float>(UINT64_MAX
)));
129 MOZ_RELEASE_ASSERT((!IsInBounds
<uint32_t, float>(UINT32_MAX
)));
130 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, float>(UINT16_MAX
)));
131 MOZ_RELEASE_ASSERT((IsInBounds
<uint8_t, float>(UINT8_MAX
)));
133 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, float>(INT64_MAX
)));
134 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, float>(INT64_MIN
)));
135 MOZ_RELEASE_ASSERT((!IsInBounds
<int32_t, float>(INT32_MAX
)));
136 MOZ_RELEASE_ASSERT((!IsInBounds
<int32_t, float>(INT32_MIN
)));
137 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, float>(INT16_MAX
)));
138 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, float>(INT16_MIN
)));
139 MOZ_RELEASE_ASSERT((IsInBounds
<int8_t, float>(INT8_MAX
)));
140 MOZ_RELEASE_ASSERT((IsInBounds
<int8_t, float>(INT8_MIN
)));
142 MOZ_RELEASE_ASSERT((!IsInBounds
<uint64_t, double>(UINT64_MAX
)));
143 MOZ_RELEASE_ASSERT((IsInBounds
<uint32_t, double>(UINT32_MAX
)));
144 MOZ_RELEASE_ASSERT((IsInBounds
<uint16_t, double>(UINT16_MAX
)));
145 MOZ_RELEASE_ASSERT((IsInBounds
<uint8_t, double>(UINT8_MAX
)));
147 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, double>(INT64_MAX
)));
148 MOZ_RELEASE_ASSERT((!IsInBounds
<int64_t, double>(INT64_MIN
)));
149 MOZ_RELEASE_ASSERT((IsInBounds
<int32_t, double>(INT32_MAX
)));
150 MOZ_RELEASE_ASSERT((IsInBounds
<int32_t, double>(INT32_MIN
)));
151 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, double>(INT16_MAX
)));
152 MOZ_RELEASE_ASSERT((IsInBounds
<int16_t, double>(INT16_MIN
)));
153 MOZ_RELEASE_ASSERT((IsInBounds
<int8_t, double>(INT8_MAX
)));
154 MOZ_RELEASE_ASSERT((IsInBounds
<int8_t, double>(INT8_MIN
)));
157 MOZ_RELEASE_ASSERT((IsInBounds
<float, uint64_t>(4.3)));
158 MOZ_RELEASE_ASSERT((AssertedCast
<uint64_t>(4.3f
) == 4u));
159 MOZ_RELEASE_ASSERT((IsInBounds
<float, uint32_t>(4.3)));
160 MOZ_RELEASE_ASSERT((AssertedCast
<uint32_t>(4.3f
) == 4u));
161 MOZ_RELEASE_ASSERT((IsInBounds
<float, uint16_t>(4.3)));
162 MOZ_RELEASE_ASSERT((AssertedCast
<uint16_t>(4.3f
) == 4u));
163 MOZ_RELEASE_ASSERT((IsInBounds
<float, uint8_t>(4.3)));
164 MOZ_RELEASE_ASSERT((AssertedCast
<uint8_t>(4.3f
) == 4u));
166 MOZ_RELEASE_ASSERT((IsInBounds
<float, int64_t>(4.3)));
167 MOZ_RELEASE_ASSERT((AssertedCast
<int64_t>(4.3f
) == 4u));
168 MOZ_RELEASE_ASSERT((IsInBounds
<float, int32_t>(4.3)));
169 MOZ_RELEASE_ASSERT((AssertedCast
<int32_t>(4.3f
) == 4u));
170 MOZ_RELEASE_ASSERT((IsInBounds
<float, int16_t>(4.3)));
171 MOZ_RELEASE_ASSERT((AssertedCast
<int16_t>(4.3f
) == 4u));
172 MOZ_RELEASE_ASSERT((IsInBounds
<float, int8_t>(4.3)));
173 MOZ_RELEASE_ASSERT((AssertedCast
<int8_t>(4.3f
) == 4u));
175 MOZ_RELEASE_ASSERT((IsInBounds
<float, int64_t>(-4.3)));
176 MOZ_RELEASE_ASSERT((AssertedCast
<int64_t>(-4.3f
) == -4));
177 MOZ_RELEASE_ASSERT((IsInBounds
<float, int32_t>(-4.3)));
178 MOZ_RELEASE_ASSERT((AssertedCast
<int32_t>(-4.3f
) == -4));
179 MOZ_RELEASE_ASSERT((IsInBounds
<float, int16_t>(-4.3)));
180 MOZ_RELEASE_ASSERT((AssertedCast
<int16_t>(-4.3f
) == -4));
181 MOZ_RELEASE_ASSERT((IsInBounds
<float, int8_t>(-4.3)));
182 MOZ_RELEASE_ASSERT((AssertedCast
<int8_t>(-4.3f
) == -4));
184 // Bound check for float to unsigned integer conversion. The parameters are
185 // espilons and offsets allowing to check boundaries, that depend on the
186 // magnitude of the numbers.
187 checkBoundariesFloating
<double, uint64_t>(2049.);
188 checkBoundariesFloating
<double, uint32_t>(1.);
189 checkBoundariesFloating
<double, uint16_t>(1.);
190 checkBoundariesFloating
<double, uint8_t>(1.);
191 // Large number because of the lack of precision of floats at this magnitude
192 checkBoundariesFloating
<float, uint64_t>(1.1e12f
);
193 checkBoundariesFloating
<float, uint32_t>(1.f
, 128u);
194 checkBoundariesFloating
<float, uint16_t>(1.f
);
195 checkBoundariesFloating
<float, uint8_t>(1.f
);
197 checkBoundariesFloating
<double, int64_t>(1025.);
198 checkBoundariesFloating
<double, int32_t>(1.);
199 checkBoundariesFloating
<double, int16_t>(1.);
200 checkBoundariesFloating
<double, int8_t>(1.);
201 // Large number because of the lack of precision of floats at this magnitude
202 checkBoundariesFloating
<float, int64_t>(1.1e12f
);
203 checkBoundariesFloating
<float, int32_t>(256.f
, 64u);
204 checkBoundariesFloating
<float, int16_t>(1.f
);
205 checkBoundariesFloating
<float, int8_t>(1.f
);
207 // Integer to floating point, boundary cases
208 MOZ_RELEASE_ASSERT(!(IsInBounds
<int64_t, float>(
209 int64_t(std::pow(2, floatMantissaBitsPlusOne
)) + 1)));
210 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, float>(
211 int64_t(std::pow(2, floatMantissaBitsPlusOne
)))));
212 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, float>(
213 int64_t(std::pow(2, floatMantissaBitsPlusOne
)) - 1)));
215 MOZ_RELEASE_ASSERT(!(IsInBounds
<int64_t, float>(
216 int64_t(-std::pow(2, floatMantissaBitsPlusOne
)) - 1)));
217 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, float>(
218 int64_t(-std::pow(2, floatMantissaBitsPlusOne
)))));
219 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, float>(
220 int64_t(-std::pow(2, floatMantissaBitsPlusOne
)) + 1)));
222 MOZ_RELEASE_ASSERT(!(IsInBounds
<int64_t, double>(
223 uint64_t(std::pow(2, doubleMantissaBitsPlusOne
)) + 1)));
224 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, double>(
225 uint64_t(std::pow(2, doubleMantissaBitsPlusOne
)))));
226 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, double>(
227 uint64_t(std::pow(2, doubleMantissaBitsPlusOne
)) - 1)));
229 MOZ_RELEASE_ASSERT(!(IsInBounds
<int64_t, double>(
230 int64_t(-std::pow(2, doubleMantissaBitsPlusOne
)) - 1)));
231 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, double>(
232 int64_t(-std::pow(2, doubleMantissaBitsPlusOne
)))));
233 MOZ_RELEASE_ASSERT((IsInBounds
<int64_t, double>(
234 int64_t(-std::pow(2, doubleMantissaBitsPlusOne
)) + 1)));
236 MOZ_RELEASE_ASSERT(!(IsInBounds
<uint64_t, double>(UINT64_MAX
)));
237 MOZ_RELEASE_ASSERT(!(IsInBounds
<int64_t, double>(INT64_MAX
)));
238 MOZ_RELEASE_ASSERT(!(IsInBounds
<int64_t, double>(INT64_MIN
)));
241 !(IsInBounds
<double, float>(std::numeric_limits
<double>::max())));
243 !(IsInBounds
<double, float>(-std::numeric_limits
<double>::max())));
252 TestFloatConversion();