Bug 1837557 - Productionize webgpu pushErrorScope/popErrorScope. r=webgpu-reviewers...
[gecko.git] / mfbt / MathAlgorithms.h
blob66aa1b9f71246f8e9aed83c0b0a60fdb500a31ff
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 /* mfbt maths algorithms. */
9 #ifndef mozilla_MathAlgorithms_h
10 #define mozilla_MathAlgorithms_h
12 #include "mozilla/Assertions.h"
14 #include <cmath>
15 #include <algorithm>
16 #include <limits.h>
17 #include <stdint.h>
18 #include <type_traits>
20 namespace mozilla {
22 namespace detail {
24 template <typename T>
25 struct AllowDeprecatedAbsFixed : std::false_type {};
27 template <>
28 struct AllowDeprecatedAbsFixed<int32_t> : std::true_type {};
29 template <>
30 struct AllowDeprecatedAbsFixed<int64_t> : std::true_type {};
32 template <typename T>
33 struct AllowDeprecatedAbs : AllowDeprecatedAbsFixed<T> {};
35 template <>
36 struct AllowDeprecatedAbs<int> : std::true_type {};
37 template <>
38 struct AllowDeprecatedAbs<long> : std::true_type {};
40 } // namespace detail
42 // DO NOT USE DeprecatedAbs. It exists only until its callers can be converted
43 // to Abs below, and it will be removed when all callers have been changed.
44 template <typename T>
45 inline std::enable_if_t<detail::AllowDeprecatedAbs<T>::value, T> DeprecatedAbs(
46 const T aValue) {
47 // The absolute value of the smallest possible value of a signed-integer type
48 // won't fit in that type (on twos-complement systems -- and we're blithely
49 // assuming we're on such systems, for the non-<stdint.h> types listed above),
50 // so assert that the input isn't that value.
52 // This is the case if: the value is non-negative; or if adding one (giving a
53 // value in the range [-maxvalue, 0]), then negating (giving a value in the
54 // range [0, maxvalue]), doesn't produce maxvalue (because in twos-complement,
55 // (minvalue + 1) == -maxvalue).
56 MOZ_ASSERT(aValue >= 0 ||
57 -(aValue + 1) != T((1ULL << (CHAR_BIT * sizeof(T) - 1)) - 1),
58 "You can't negate the smallest possible negative integer!");
59 return aValue >= 0 ? aValue : -aValue;
62 namespace detail {
64 template <typename T, typename = void>
65 struct AbsReturnType;
67 template <typename T>
68 struct AbsReturnType<
69 T, std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>>> {
70 using Type = std::make_unsigned_t<T>;
73 template <typename T>
74 struct AbsReturnType<T, std::enable_if_t<std::is_floating_point_v<T>>> {
75 using Type = T;
78 } // namespace detail
80 template <typename T>
81 inline constexpr typename detail::AbsReturnType<T>::Type Abs(const T aValue) {
82 using ReturnType = typename detail::AbsReturnType<T>::Type;
83 return aValue >= 0 ? ReturnType(aValue) : ~ReturnType(aValue) + 1;
86 template <>
87 inline float Abs<float>(const float aFloat) {
88 return std::fabs(aFloat);
91 template <>
92 inline double Abs<double>(const double aDouble) {
93 return std::fabs(aDouble);
96 template <>
97 inline long double Abs<long double>(const long double aLongDouble) {
98 return std::fabs(aLongDouble);
101 } // namespace mozilla
103 #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \
104 defined(_M_X64) || defined(_M_ARM64))
105 # define MOZ_BITSCAN_WINDOWS
107 # include <intrin.h>
108 # pragma intrinsic(_BitScanForward, _BitScanReverse)
110 # if defined(_M_AMD64) || defined(_M_X64) || defined(_M_ARM64)
111 # define MOZ_BITSCAN_WINDOWS64
112 # pragma intrinsic(_BitScanForward64, _BitScanReverse64)
113 # endif
115 #endif
117 namespace mozilla {
119 namespace detail {
121 #if defined(MOZ_BITSCAN_WINDOWS)
123 inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) {
124 unsigned long index;
125 if (!_BitScanReverse(&index, static_cast<unsigned long>(aValue))) return 32;
126 return uint_fast8_t(31 - index);
129 inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) {
130 unsigned long index;
131 if (!_BitScanForward(&index, static_cast<unsigned long>(aValue))) return 32;
132 return uint_fast8_t(index);
135 inline uint_fast8_t CountPopulation32(uint32_t aValue) {
136 uint32_t x = aValue - ((aValue >> 1) & 0x55555555);
137 x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
138 return (((x + (x >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
140 inline uint_fast8_t CountPopulation64(uint64_t aValue) {
141 return uint_fast8_t(CountPopulation32(aValue & 0xffffffff) +
142 CountPopulation32(aValue >> 32));
145 inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) {
146 # if defined(MOZ_BITSCAN_WINDOWS64)
147 unsigned long index;
148 if (!_BitScanReverse64(&index, static_cast<unsigned __int64>(aValue)))
149 return 64;
150 return uint_fast8_t(63 - index);
151 # else
152 uint32_t hi = uint32_t(aValue >> 32);
153 if (hi != 0) {
154 return CountLeadingZeroes32(hi);
156 return 32u + CountLeadingZeroes32(uint32_t(aValue));
157 # endif
160 inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) {
161 # if defined(MOZ_BITSCAN_WINDOWS64)
162 unsigned long index;
163 if (!_BitScanForward64(&index, static_cast<unsigned __int64>(aValue)))
164 return 64;
165 return uint_fast8_t(index);
166 # else
167 uint32_t lo = uint32_t(aValue);
168 if (lo != 0) {
169 return CountTrailingZeroes32(lo);
171 return 32u + CountTrailingZeroes32(uint32_t(aValue >> 32));
172 # endif
175 #elif defined(__clang__) || defined(__GNUC__)
177 # if defined(__clang__)
178 # if !__has_builtin(__builtin_ctz) || !__has_builtin(__builtin_clz)
179 # error "A clang providing __builtin_c[lt]z is required to build"
180 # endif
181 # else
182 // gcc has had __builtin_clz and friends since 3.4: no need to check.
183 # endif
185 inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) {
186 return static_cast<uint_fast8_t>(__builtin_clz(aValue));
189 inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) {
190 return static_cast<uint_fast8_t>(__builtin_ctz(aValue));
193 inline uint_fast8_t CountPopulation32(uint32_t aValue) {
194 return static_cast<uint_fast8_t>(__builtin_popcount(aValue));
197 inline uint_fast8_t CountPopulation64(uint64_t aValue) {
198 return static_cast<uint_fast8_t>(__builtin_popcountll(aValue));
201 inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) {
202 return static_cast<uint_fast8_t>(__builtin_clzll(aValue));
205 inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) {
206 return static_cast<uint_fast8_t>(__builtin_ctzll(aValue));
209 #else
210 # error "Implement these!"
211 inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) = delete;
212 inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) = delete;
213 inline uint_fast8_t CountPopulation32(uint32_t aValue) = delete;
214 inline uint_fast8_t CountPopulation64(uint64_t aValue) = delete;
215 inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) = delete;
216 inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) = delete;
217 #endif
219 } // namespace detail
222 * Compute the number of high-order zero bits in the NON-ZERO number |aValue|.
223 * That is, looking at the bitwise representation of the number, with the
224 * highest- valued bits at the start, return the number of zeroes before the
225 * first one is observed.
227 * CountLeadingZeroes32(0xF0FF1000) is 0;
228 * CountLeadingZeroes32(0x7F8F0001) is 1;
229 * CountLeadingZeroes32(0x3FFF0100) is 2;
230 * CountLeadingZeroes32(0x1FF50010) is 3; and so on.
232 inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) {
233 MOZ_ASSERT(aValue != 0);
234 return detail::CountLeadingZeroes32(aValue);
238 * Compute the number of low-order zero bits in the NON-ZERO number |aValue|.
239 * That is, looking at the bitwise representation of the number, with the
240 * lowest- valued bits at the start, return the number of zeroes before the
241 * first one is observed.
243 * CountTrailingZeroes32(0x0100FFFF) is 0;
244 * CountTrailingZeroes32(0x7000FFFE) is 1;
245 * CountTrailingZeroes32(0x0080FFFC) is 2;
246 * CountTrailingZeroes32(0x0080FFF8) is 3; and so on.
248 inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) {
249 MOZ_ASSERT(aValue != 0);
250 return detail::CountTrailingZeroes32(aValue);
254 * Compute the number of one bits in the number |aValue|,
256 inline uint_fast8_t CountPopulation32(uint32_t aValue) {
257 return detail::CountPopulation32(aValue);
260 /** Analogous to CountPopulation32, but for 64-bit numbers */
261 inline uint_fast8_t CountPopulation64(uint64_t aValue) {
262 return detail::CountPopulation64(aValue);
265 /** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */
266 inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) {
267 MOZ_ASSERT(aValue != 0);
268 return detail::CountLeadingZeroes64(aValue);
271 /** Analogous to CountTrailingZeroes32, but for 64-bit numbers. */
272 inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) {
273 MOZ_ASSERT(aValue != 0);
274 return detail::CountTrailingZeroes64(aValue);
277 namespace detail {
279 template <typename T, size_t Size = sizeof(T)>
280 class CeilingLog2;
282 template <typename T>
283 class CeilingLog2<T, 4> {
284 public:
285 static uint_fast8_t compute(const T aValue) {
286 // Check for <= 1 to avoid the == 0 undefined case.
287 return aValue <= 1 ? 0u : 32u - CountLeadingZeroes32(aValue - 1);
291 template <typename T>
292 class CeilingLog2<T, 8> {
293 public:
294 static uint_fast8_t compute(const T aValue) {
295 // Check for <= 1 to avoid the == 0 undefined case.
296 return aValue <= 1 ? 0u : 64u - CountLeadingZeroes64(aValue - 1);
300 } // namespace detail
303 * Compute the log of the least power of 2 greater than or equal to |aValue|.
305 * CeilingLog2(0..1) is 0;
306 * CeilingLog2(2) is 1;
307 * CeilingLog2(3..4) is 2;
308 * CeilingLog2(5..8) is 3;
309 * CeilingLog2(9..16) is 4; and so on.
311 template <typename T>
312 inline uint_fast8_t CeilingLog2(const T aValue) {
313 return detail::CeilingLog2<T>::compute(aValue);
316 /** A CeilingLog2 variant that accepts only size_t. */
317 inline uint_fast8_t CeilingLog2Size(size_t aValue) {
318 return CeilingLog2(aValue);
321 namespace detail {
323 template <typename T, size_t Size = sizeof(T)>
324 class FloorLog2;
326 template <typename T>
327 class FloorLog2<T, 4> {
328 public:
329 static uint_fast8_t compute(const T aValue) {
330 return 31u - CountLeadingZeroes32(aValue | 1);
334 template <typename T>
335 class FloorLog2<T, 8> {
336 public:
337 static uint_fast8_t compute(const T aValue) {
338 return 63u - CountLeadingZeroes64(aValue | 1);
342 } // namespace detail
345 * Compute the log of the greatest power of 2 less than or equal to |aValue|.
347 * FloorLog2(0..1) is 0;
348 * FloorLog2(2..3) is 1;
349 * FloorLog2(4..7) is 2;
350 * FloorLog2(8..15) is 3; and so on.
352 template <typename T>
353 inline constexpr uint_fast8_t FloorLog2(const T aValue) {
354 return detail::FloorLog2<T>::compute(aValue);
357 /** A FloorLog2 variant that accepts only size_t. */
358 inline uint_fast8_t FloorLog2Size(size_t aValue) { return FloorLog2(aValue); }
361 * Compute the smallest power of 2 greater than or equal to |x|. |x| must not
362 * be so great that the computed value would overflow |size_t|.
364 inline size_t RoundUpPow2(size_t aValue) {
365 MOZ_ASSERT(aValue <= (size_t(1) << (sizeof(size_t) * CHAR_BIT - 1)),
366 "can't round up -- will overflow!");
367 return size_t(1) << CeilingLog2(aValue);
371 * Rotates the bits of the given value left by the amount of the shift width.
373 template <typename T>
374 MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW inline T RotateLeft(const T aValue,
375 uint_fast8_t aShift) {
376 static_assert(std::is_unsigned_v<T>, "Rotates require unsigned values");
378 MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!");
379 MOZ_ASSERT(aShift > 0,
380 "Rotation by value length is undefined behavior, but compilers "
381 "do not currently fold a test into the rotate instruction. "
382 "Please remove this restriction when compilers optimize the "
383 "zero case (http://blog.regehr.org/archives/1063).");
385 return (aValue << aShift) | (aValue >> (sizeof(T) * CHAR_BIT - aShift));
389 * Rotates the bits of the given value right by the amount of the shift width.
391 template <typename T>
392 MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW inline T RotateRight(const T aValue,
393 uint_fast8_t aShift) {
394 static_assert(std::is_unsigned_v<T>, "Rotates require unsigned values");
396 MOZ_ASSERT(aShift < sizeof(T) * CHAR_BIT, "Shift value is too large!");
397 MOZ_ASSERT(aShift > 0,
398 "Rotation by value length is undefined behavior, but compilers "
399 "do not currently fold a test into the rotate instruction. "
400 "Please remove this restriction when compilers optimize the "
401 "zero case (http://blog.regehr.org/archives/1063).");
403 return (aValue >> aShift) | (aValue << (sizeof(T) * CHAR_BIT - aShift));
407 * Returns true if |x| is a power of two.
408 * Zero is not an integer power of two. (-Inf is not an integer)
410 template <typename T>
411 constexpr bool IsPowerOfTwo(T x) {
412 static_assert(std::is_unsigned_v<T>, "IsPowerOfTwo requires unsigned values");
413 return x && (x & (x - 1)) == 0;
416 template <typename T>
417 inline T Clamp(const T aValue, const T aMin, const T aMax) {
418 static_assert(std::is_integral_v<T>,
419 "Clamp accepts only integral types, so that it doesn't have"
420 " to distinguish differently-signed zeroes (which users may"
421 " or may not care to distinguish, likely at a perf cost) or"
422 " to decide how to clamp NaN or a range with a NaN"
423 " endpoint.");
424 MOZ_ASSERT(aMin <= aMax);
426 if (aValue <= aMin) return aMin;
427 if (aValue >= aMax) return aMax;
428 return aValue;
431 template <typename T>
432 inline uint_fast8_t CountTrailingZeroes(T aValue) {
433 static_assert(sizeof(T) <= 8);
434 static_assert(std::is_integral_v<T>);
435 // This casts to 32-bits
436 if constexpr (sizeof(T) <= 4) {
437 return CountTrailingZeroes32(aValue);
439 // This doesn't
440 if constexpr (sizeof(T) == 8) {
441 return CountTrailingZeroes64(aValue);
445 // Greatest Common Divisor, from
446 // https://en.wikipedia.org/wiki/Binary_GCD_algorithm#Implementation
447 template <typename T>
448 MOZ_ALWAYS_INLINE T GCD(T aA, T aB) {
449 static_assert(std::is_integral_v<T>);
451 MOZ_ASSERT(aA >= 0);
452 MOZ_ASSERT(aB >= 0);
454 if (aA == 0) {
455 return aB;
457 if (aB == 0) {
458 return aA;
461 T az = CountTrailingZeroes(aA);
462 T bz = CountTrailingZeroes(aB);
463 T shift = std::min<T>(az, bz);
464 aA >>= az;
465 aB >>= bz;
467 while (aA != 0) {
468 if constexpr (!std::is_signed_v<T>) {
469 if (aA < aB) {
470 std::swap(aA, aB);
473 T diff = aA - aB;
474 if constexpr (std::is_signed_v<T>) {
475 aB = std::min<T>(aA, aB);
477 if constexpr (std::is_signed_v<T>) {
478 aA = std::abs(diff);
479 } else {
480 aA = diff;
482 if (aA) {
483 aA >>= CountTrailingZeroes(aA);
487 return aB << shift;
490 } /* namespace mozilla */
492 #endif /* mozilla_MathAlgorithms_h */