blob: 79e5d13c06d5f469ed1ee6d058c5915f2def98df [file] [edit]
/*
* Copyright (C) 2006-2024 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <algorithm>
#include <bit>
#include <climits>
#include <cmath>
#include <float.h>
#include <limits>
#include <numbers>
#include <optional>
#include <stdint.h>
#include <stdlib.h>
#include <wtf/CheckedArithmetic.h>
#include <wtf/StdLibExtras.h>
#if CPU(ARM64)
#include <arm_neon.h>
#endif
#if CPU(X86_64)
#include <emmintrin.h>
#endif
#if OS(OPENBSD)
#include <sys/types.h>
#include <machine/ieee.h>
#endif
constexpr double piOverTwoDouble = std::numbers::pi / 2;
constexpr float piOverTwoFloat = static_cast<float>(piOverTwoDouble);
constexpr double piOverFourDouble = std::numbers::pi / 4;
constexpr float piOverFourFloat = static_cast<float>(piOverFourDouble);
#if OS(WINDOWS)
// Work around a bug in Win, where atan2(+-infinity, +-infinity) yields NaN instead of specific values.
extern "C" inline double wtf_atan2(double x, double y)
{
constexpr double posInf = std::numeric_limits<double>::infinity();
constexpr double negInf = -std::numeric_limits<double>::infinity();
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
double result = nan;
if (x == posInf && y == posInf)
result = piOverFourDouble;
else if (x == posInf && y == negInf)
result = 3 * piOverFourDouble;
else if (x == negInf && y == posInf)
result = -piOverFourDouble;
else if (x == negInf && y == negInf)
result = -3 * piOverFourDouble;
else
result = ::atan2(x, y);
return result;
}
#define atan2(x, y) wtf_atan2(x, y)
#endif // OS(WINDOWS)
constexpr double radiansPerDegreeDouble = std::numbers::pi / 180.0;
constexpr double degreesPerRadianDouble = 180.0 / std::numbers::pi;
constexpr double gradientsPerDegreeDouble = 400.0 / 360.0;
constexpr double degreesPerGradientDouble = 360.0 / 400.0;
constexpr double turnsPerDegreeDouble = 1.0 / 360.0;
constexpr double degreesPerTurnDouble = 360.0;
constexpr double radiansPerTurnDouble = 2.0 * std::numbers::pi;
constexpr double deg2rad(double d) { return d * radiansPerDegreeDouble; }
constexpr double rad2deg(double r) { return r * degreesPerRadianDouble; }
constexpr double deg2grad(double d) { return d * gradientsPerDegreeDouble; }
constexpr double grad2deg(double g) { return g * degreesPerGradientDouble; }
constexpr double deg2turn(double d) { return d * turnsPerDegreeDouble; }
constexpr double turn2deg(double t) { return t * degreesPerTurnDouble; }
// Note that these differ from the casting the double values above in their rounding errors.
constexpr float radiansPerDegreeFloat = std::numbers::pi_v<float> / 180.0f;
constexpr float degreesPerRadianFloat = 180.0f / std::numbers::pi_v<float>;
constexpr float gradientsPerDegreeFloat= 400.0f / 360.0f;
constexpr float degreesPerGradientFloat = 360.0f / 400.0f;
constexpr float turnsPerDegreeFloat = 1.0f / 360.0f;
constexpr float degreesPerTurnFloat = 360.0f;
constexpr float radiansPerTurnFloat = 2.0f * std::numbers::pi_v<float>;
constexpr float deg2rad(float d) { return d * radiansPerDegreeFloat; }
constexpr float rad2deg(float r) { return r * degreesPerRadianFloat; }
constexpr float deg2grad(float d) { return d * gradientsPerDegreeFloat; }
constexpr float grad2deg(float g) { return g * degreesPerGradientFloat; }
constexpr float deg2turn(float d) { return d * turnsPerDegreeFloat; }
constexpr float turn2deg(float t) { return t * degreesPerTurnFloat; }
// Treat these as conversions through the canonical unit for angles, which is degrees.
constexpr double rad2grad(double r) { return deg2grad(rad2deg(r)); }
constexpr double grad2rad(double g) { return deg2rad(grad2deg(g)); }
constexpr double turn2grad(double t) { return deg2grad(turn2deg(t)); }
constexpr double grad2turn(double g) { return deg2turn(grad2deg(g)); }
constexpr double turn2rad(double t) { return deg2rad(turn2deg(t)); }
constexpr double rad2turn(double r) { return deg2turn(rad2deg(r)); }
constexpr float rad2grad(float r) { return deg2grad(rad2deg(r)); }
constexpr float grad2rad(float g) { return deg2rad(grad2deg(g)); }
constexpr float turn2grad(float t) { return deg2grad(turn2deg(t)); }
constexpr float grad2turn(float g) { return deg2turn(grad2deg(g)); }
constexpr float turn2rad(float t) { return deg2rad(turn2deg(t)); }
constexpr float rad2turn(float r) { return deg2turn(rad2deg(r)); }
namespace WTF {
// FIXME: Replace with std::isnan() once std::isnan() is constexpr. requires C++23
template<std::floating_point T>
constexpr bool isNaNConstExpr(T value)
{
#if COMPILER_HAS_CLANG_BUILTIN(__builtin_isnan)
return __builtin_isnan(value);
#else
return value != value;
#endif
}
template<std::integral T>
constexpr bool isNaNConstExpr(T)
{
return false;
}
// FIXME: Replace with std::fabs() once std::fabs() is constexpr. requires C++23
template<std::floating_point T>
constexpr T fabsConstExpr(T value)
{
if (value != value)
return value;
if (!value)
return 0.0; // -0.0 should be converted to +0.0
if (value < 0.0)
return -value;
return value;
}
} // namespace WTF
inline double roundTowardsPositiveInfinity(double value) { return std::floor(value + 0.5); }
inline float roundTowardsPositiveInfinity(float value) { return std::floor(value + 0.5f); }
// C23 roundeven polyfill.
#if CPU(ARM64)
// On ARM64, __builtin_roundeven(f) inlines to frintn. On x86_64, Clang
// may emit a library call to roundeven which is C23 and not
// universally available.
inline float roundevenf(float value) { return __builtin_roundevenf(value); }
inline double roundeven(double value) { return __builtin_roundeven(value); }
#else
inline float roundevenf(float value)
{
float rounded = std::round(value);
if (std::fabs(value - rounded) == 0.5f) {
if (std::fmod(rounded, 2.0f) != 0.0f)
return rounded - std::copysign(1.0f, value);
}
return rounded;
}
inline double roundeven(double value)
{
double rounded = std::round(value);
if (std::fabs(value - rounded) == 0.5) {
if (std::fmod(rounded, 2.0) != 0.0)
return rounded - std::copysign(1.0, value);
}
return rounded;
}
#endif
// std::numeric_limits<T>::min() returns the smallest positive value for floating point types
template<typename T> consteval T defaultMinimumForClamp() { return std::numeric_limits<T>::min(); }
template<> consteval float defaultMinimumForClamp() { return -std::numeric_limits<float>::max(); }
template<> consteval double defaultMinimumForClamp() { return -std::numeric_limits<double>::max(); }
template<typename T> consteval T defaultMaximumForClamp() { return std::numeric_limits<T>::max(); }
// Same type in and out.
template<typename TargetType, typename SourceType>
requires std::same_as<TargetType, SourceType>
constexpr TargetType clampTo(SourceType value, TargetType min = defaultMinimumForClamp<TargetType>(), TargetType max = defaultMaximumForClamp<TargetType>())
{
if (value >= max)
return max;
if (value <= min)
return min;
return value;
}
// Floating point source.
template<typename TargetType, typename SourceType>
requires (!std::same_as<TargetType, SourceType>
&& std::floating_point<SourceType>
&& !(std::floating_point<TargetType> && sizeof(TargetType) > sizeof(SourceType)))
constexpr TargetType clampTo(SourceType value, TargetType min = defaultMinimumForClamp<TargetType>(), TargetType max = defaultMaximumForClamp<TargetType>())
{
if (value >= static_cast<SourceType>(max))
return max;
// This will return min if value is NaN.
if (!(value > static_cast<SourceType>(min)))
return min;
return static_cast<TargetType>(value);
}
template<typename TargetType, typename SourceType>
requires (!std::same_as<TargetType, SourceType>
&& std::floating_point<SourceType>
&& std::floating_point<TargetType>
&& sizeof(TargetType) > sizeof(SourceType))
constexpr TargetType clampTo(SourceType value, TargetType min = defaultMinimumForClamp<TargetType>(), TargetType max = defaultMaximumForClamp<TargetType>())
{
TargetType convertedValue = static_cast<TargetType>(value);
if (convertedValue >= max)
return max;
if (convertedValue <= min)
return min;
return convertedValue;
}
// Source and Target have the same sign and Source is larger or equal to Target
template<typename TargetType, typename SourceType>
requires (!std::same_as<TargetType, SourceType>
&& std::integral<SourceType>
&& std::integral<TargetType>
&& std::signed_integral<TargetType> == std::signed_integral<SourceType>
&& sizeof(SourceType) >= sizeof(TargetType))
constexpr TargetType clampTo(SourceType value, TargetType min = defaultMinimumForClamp<TargetType>(), TargetType max = defaultMaximumForClamp<TargetType>())
{
if (value >= static_cast<SourceType>(max))
return max;
if (value <= static_cast<SourceType>(min))
return min;
return static_cast<TargetType>(value);
}
// Clamping a unsigned integer to the max signed value.
template<typename TargetType, typename SourceType>
requires (!std::same_as<TargetType, SourceType>
&& std::unsigned_integral<SourceType>
&& std::signed_integral<TargetType>
&& sizeof(SourceType) >= sizeof(TargetType))
constexpr TargetType clampTo(SourceType value)
{
TargetType max = std::numeric_limits<TargetType>::max();
if (value >= static_cast<SourceType>(max))
return max;
return static_cast<TargetType>(value);
}
// Clamping a signed integer into a valid unsigned integer.
template<typename TargetType, typename SourceType>
requires (!std::same_as<TargetType, SourceType>
&& std::unsigned_integral<TargetType>
&& std::signed_integral<SourceType>
&& sizeof(SourceType) == sizeof(TargetType))
constexpr TargetType clampTo(SourceType value)
{
if (value < 0)
return 0;
return static_cast<TargetType>(value);
}
template<typename TargetType, typename SourceType>
requires (!std::same_as<TargetType, SourceType>
&& std::unsigned_integral<TargetType>
&& std::signed_integral<SourceType>
&& sizeof(SourceType) > sizeof(TargetType))
constexpr TargetType clampTo(SourceType value, TargetType min = defaultMinimumForClamp<TargetType>(), TargetType max = defaultMaximumForClamp<TargetType>())
{
if (value >= static_cast<SourceType>(max))
return max;
if (value <= static_cast<SourceType>(min))
return min;
return static_cast<TargetType>(value);
}
constexpr unsigned clampToUnsigned(double value)
{
return clampTo<unsigned>(value);
}
constexpr float clampToFloat(double value)
{
return clampTo<float>(value);
}
constexpr int clampToPositiveInteger(double value)
{
return clampTo<int>(value, 0);
}
// Explicitly accept 64bit result when clamping double value.
// Keep in mind that double can only represent 53bit integer precisely.
template<typename T>
constexpr T clampToAccepting64(double value, T min = defaultMinimumForClamp<T>(), T max = defaultMaximumForClamp<T>())
{
return (value >= static_cast<double>(max)) ? max : ((value <= static_cast<double>(min)) ? min : static_cast<T>(value));
}
constexpr bool isWithinIntRange(float x)
{
return x > static_cast<float>(std::numeric_limits<int>::min()) && x < static_cast<float>(std::numeric_limits<int>::max());
}
constexpr float normalizedFloat(float value)
{
if (value > 0 && value < std::numeric_limits<float>::min())
return std::numeric_limits<float>::min();
if (value < 0 && value > -std::numeric_limits<float>::min())
return -std::numeric_limits<float>::min();
return value;
}
template<typename T>
constexpr bool hasOneBitSet(T value)
{
return !((value - 1) & value) && value;
}
template<typename T>
constexpr bool hasZeroOrOneBitsSet(T value)
{
return !((value - 1) & value);
}
template<typename T>
constexpr bool hasTwoOrMoreBitsSet(T value)
{
return !hasZeroOrOneBitsSet(value);
}
// "Determine if a word has a zero byte" at https://graphics.stanford.edu/~seander/bithacks.html
// (formula credited there to Alan Mycroft, comp.lang.c, April 27 1987).
template<typename T>
constexpr bool hasZeroByte(T value)
{
static_assert(std::is_unsigned_v<T>);
constexpr T lowBits = static_cast<T>(static_cast<T>(~static_cast<T>(0)) / 0xFF);
constexpr T highBits = static_cast<T>(lowBits * 0x80);
return (value - lowBits) & ~value & highBits;
}
// "Interleave bits by Binary Magic Numbers" at https://graphics.stanford.edu/~seander/bithacks.html.
constexpr uint64_t zeroExtendBytesToHalfwords(uint32_t value)
{
uint64_t result = value;
result = (result | (result << 16)) & 0x0000FFFF0000FFFFULL;
result = (result | (result << 8)) & 0x00FF00FF00FF00FFULL;
return result;
}
template<typename T>
constexpr T divideRoundedUp(T a, T b)
{
// Mathematically equivalent to (a + b - 1) / b, but does not overflow
// when a is close to the maximum representable value of T.
return a / b + !!(a % b);
}
template<typename T>
constexpr T timesThreePlusOneDividedByTwo(T value)
{
// Mathematically equivalent to:
// (value * 3 + 1) / 2;
// or:
// (unsigned)ceil(value * 1.5));
// This form is not prone to internal overflow.
return value + (value >> 1) + (value & 1);
}
template<typename T>
constexpr bool isNotZeroAndOrdered(T value)
{
return value > 0.0 || value < 0.0;
}
template<typename T>
constexpr bool isZeroOrUnordered(T value)
{
return !isNotZeroAndOrdered(value);
}
template<typename T>
constexpr bool isGreaterThanNonZeroPowerOfTwo(T value, unsigned power)
{
// The crazy way of testing of index >= 2 ** power
// (where I use ** to denote pow()).
return !!((value >> 1) >> (power - 1));
}
template<typename T>
constexpr bool isMultipleOf(unsigned factor, T value)
{
return factor && !(value % factor);
}
template<typename T> constexpr bool isLessThan(const T& a, const T& b) { return a < b; }
template<typename T> constexpr bool isLessThanEqual(const T& a, const T& b) { return a <= b; }
template<typename T> constexpr bool isGreaterThan(const T& a, const T& b) { return a > b; }
template<typename T> constexpr bool isGreaterThanEqual(const T& a, const T& b) { return a >= b; }
template<typename T> constexpr bool isInRange(const T& a, const T& min, const T& max) { return a >= min && a <= max; }
// decompose 'number' to its sign, exponent, and mantissa components.
// The result is interpreted as:
// (sign ? -1 : 1) * pow(2, exponent) * (mantissa / (1 << 52))
inline void decomposeDouble(double number, bool& sign, int32_t& exponent, uint64_t& mantissa)
{
ASSERT(std::isfinite(number));
sign = std::signbit(number);
uint64_t bits = std::bit_cast<uint64_t>(number);
exponent = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff;
mantissa = bits & 0xFFFFFFFFFFFFFull;
// Check for zero/denormal values; if so, adjust the exponent,
// if not insert the implicit, omitted leading 1 bit.
if (exponent == -0x3ff)
exponent = mantissa ? -0x3fe : 0;
else
mantissa |= 0x10000000000000ull;
}
template<typename T> constexpr unsigned countOfBits = sizeof(T) * CHAR_BIT;
template<typename T> constexpr unsigned countOfMagnitudeBits = countOfBits<T> - std::is_signed_v<T>;
constexpr float powerOfTwo(unsigned e)
{
float p = 1;
while (e--)
p *= 2;
return p;
}
template<typename T> constexpr float maxPlusOne = powerOfTwo(countOfMagnitudeBits<T>);
// Calculate d % 2^{64}.
inline void doubleToInteger(double d, unsigned long long& value)
{
if (std::isnan(d) || std::isinf(d))
value = 0;
else {
// -2^{64} < fmodValue < 2^{64}.
double fmodValue = fmod(trunc(d), maxPlusOne<unsigned long long>);
if (fmodValue >= 0) {
// 0 <= fmodValue < 2^{64}.
// 0 <= value < 2^{64}. This cast causes no loss.
value = static_cast<unsigned long long>(fmodValue);
} else {
// -2^{64} < fmodValue < 0.
// 0 < fmodValueInUnsignedLongLong < 2^{64}. This cast causes no loss.
unsigned long long fmodValueInUnsignedLongLong = static_cast<unsigned long long>(-fmodValue);
// -1 < (std::numeric_limits<unsigned long long>::max() - fmodValueInUnsignedLongLong) < 2^{64} - 1.
// 0 < value < 2^{64}.
value = std::numeric_limits<unsigned long long>::max() - fmodValueInUnsignedLongLong + 1;
}
}
}
namespace WTF {
constexpr auto roundUpToPowerOfTwo(auto v)
{
return std::bit_ceil(v);
}
constexpr bool isPowerOfTwo(auto value)
{
return std::has_single_bit(value);
}
constexpr unsigned maskForSize(unsigned size)
{
if (!size)
return 0;
return roundUpToPowerOfTwo(size) - 1;
}
constexpr unsigned fastLog2(unsigned i)
{
if (!i)
return 0;
constexpr int unsignedBitWidth = std::numeric_limits<unsigned>::digits - 1;
unsigned log2 = unsignedBitWidth - std::countl_zero(i);
if (i & (i - 1))
log2 += 1;
return log2;
}
constexpr unsigned fastLog2(uint64_t value)
{
unsigned high = static_cast<unsigned>(value >> 32);
if (high)
return fastLog2(high) + 32;
return fastLog2(static_cast<unsigned>(value));
}
template<std::floating_point T>
constexpr T safeFPDivision(T u, T v)
{
// Protect against overflow / underflow.
if (v < 1 && u > v * std::numeric_limits<T>::max())
return std::numeric_limits<T>::max();
if (v > 1 && u < v * std::numeric_limits<T>::min())
return 0;
return u / v;
}
// Floating point numbers comparison:
// u is "essentially equal" [1][2] to v if: | u - v | / |u| <= e and | u - v | / |v| <= e
//
// [1] Knuth, D. E. "Accuracy of Floating Point Arithmetic." The Art of Computer Programming. 3rd ed. Vol. 2.
// Boston: Addison-Wesley, 1998. 229-45.
// [2] http://www.boost.org/doc/libs/1_34_0/libs/test/doc/components/test_tools/floating_point_comparison.html
template<std::floating_point T>
constexpr bool areEssentiallyEqual(T u, T v, T epsilon = std::numeric_limits<T>::epsilon())
{
if (u == v)
return true;
const T delta = std::abs(u - v);
return safeFPDivision(delta, std::abs(u)) <= epsilon && safeFPDivision(delta, std::abs(v)) <= epsilon;
}
// Match behavior of Math.min, where NaN is returned if either argument is NaN.
template<std::floating_point T>
constexpr T nanPropagatingMin(T a, T b)
{
return isNaNConstExpr(a) || isNaNConstExpr(b) ? std::numeric_limits<T>::quiet_NaN() : std::min(a, b);
}
// Match behavior of Math.max, where NaN is returned if either argument is NaN.
template<std::floating_point T>
constexpr T nanPropagatingMax(T a, T b)
{
return isNaNConstExpr(a) || isNaNConstExpr(b) ? std::numeric_limits<T>::quiet_NaN() : std::max(a, b);
}
constexpr bool isIntegral(float value)
{
return !std::isinf(value) && std::trunc(value) == value;
}
template<typename T>
constexpr void incrementWithSaturation(T& value)
{
if (value != std::numeric_limits<T>::max())
++value;
}
template<typename T>
constexpr T leftShiftWithSaturation(T value, unsigned shiftAmount, T max = std::numeric_limits<T>::max())
{
T result = value << shiftAmount;
// We will have saturated if shifting right doesn't recover the original value.
if (result >> shiftAmount != value)
return max;
if (result > max)
return max;
return result;
}
// Check if two ranges overlap assuming that neither range is empty.
template<typename T>
constexpr bool nonEmptyRangesOverlap(T leftMin, T leftMax, T rightMin, T rightMax)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(leftMin < leftMax);
ASSERT_UNDER_CONSTEXPR_CONTEXT(rightMin < rightMax);
return leftMax > rightMin && rightMax > leftMin;
}
// Pass ranges with the min being inclusive and the max being exclusive. For example, this should
// return false:
//
// rangesOverlap(0, 8, 8, 16)
template<typename T>
constexpr bool rangesOverlap(T leftMin, T leftMax, T rightMin, T rightMax)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(leftMin <= leftMax);
ASSERT_UNDER_CONSTEXPR_CONTEXT(rightMin <= rightMax);
// Empty ranges interfere with nothing.
if (leftMin == leftMax)
return false;
if (rightMin == rightMax)
return false;
return nonEmptyRangesOverlap(leftMin, leftMax, rightMin, rightMax);
}
template<typename VectorType, typename RandomFunc>
constexpr void shuffleVector(VectorType& vector, size_t size, const RandomFunc& randomFunc)
{
for (size_t i = 0; i + 1 < size; ++i)
std::swap(vector[i], vector[i + randomFunc(size - i)]);
}
template<typename VectorType, typename RandomFunc>
constexpr void shuffleVector(VectorType& vector, const RandomFunc& randomFunc)
{
shuffleVector(vector, vector.size(), randomFunc);
}
template<typename T>
constexpr unsigned clz(T value)
{
return std::countl_zero(unsignedCast(value));
}
template<typename T>
constexpr unsigned ctz(T value)
{
return std::countr_zero(unsignedCast(value));
}
template<typename T>
constexpr unsigned getLSBSet(T t)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(t);
return ctz(t);
}
template<typename T>
constexpr unsigned getMSBSet(T t)
{
constexpr unsigned bitSize = sizeof(T) * CHAR_BIT;
ASSERT_UNDER_CONSTEXPR_CONTEXT(t);
return bitSize - 1 - clz(t);
}
inline uint32_t reverseBits32(uint32_t value)
{
#if CPU(ARM64)
uint32_t result;
__asm__("rbit %w0, %w1"
: "=r"(result)
: "r"(value));
return result;
#else
value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1);
value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2);
value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4);
value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8);
return (value >> 16) | (value << 16);
#endif
}
// For use in places where we could negate std::numeric_limits<T>::min and would like to avoid UB.
template<std::integral T>
constexpr T negate(T v)
{
return static_cast<T>(~unsignedCast(v) + 1U);
}
template<typename BitsType, typename InputType>
constexpr bool isIdentical(InputType left, InputType right)
{
return std::bit_cast<BitsType>(left) == std::bit_cast<BitsType>(right);
}
constexpr bool isIdentical(int32_t left, int32_t right)
{
return isIdentical<int32_t>(left, right);
}
constexpr bool isIdentical(int64_t left, int64_t right)
{
return isIdentical<int64_t>(left, right);
}
constexpr bool isIdentical(double left, double right)
{
return isIdentical<int64_t>(left, right);
}
constexpr bool isIdentical(float left, float right)
{
return isIdentical<int32_t>(left, right);
}
template<typename ResultType, typename InputType, typename BitsType>
constexpr bool isRepresentableAsImpl(InputType originalValue)
{
// Convert the original value to the desired result type.
ResultType result = static_cast<ResultType>(originalValue);
// Convert the converted value back to the original type. The original value is representable
// using the new type if such round-tripping doesn't lose bits.
InputType newValue = static_cast<InputType>(result);
return isIdentical<BitsType>(originalValue, newValue);
}
template<typename ResultType>
constexpr bool isRepresentableAs(int32_t value)
{
return isRepresentableAsImpl<ResultType, int32_t, int32_t>(value);
}
template<typename ResultType>
constexpr bool isRepresentableAs(int64_t value)
{
return isRepresentableAsImpl<ResultType, int64_t, int64_t>(value);
}
template<typename ResultType>
constexpr bool isRepresentableAs(size_t value)
{
return isRepresentableAsImpl<ResultType, size_t, size_t>(value);
}
template<typename ResultType>
constexpr bool isRepresentableAs(double value)
{
return isRepresentableAsImpl<ResultType, double, int64_t>(value);
}
template<typename T>
ALWAYS_INLINE constexpr T roundUpToMultipleOfImpl(size_t divisor, T x)
{
T remainderMask = static_cast<T>(divisor) - 1;
return (x + remainderMask) & ~remainderMask;
}
// Efficient implementation that takes advantage of powers of two.
template<typename T>
constexpr T roundUpToMultipleOf(size_t divisor, T x)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(divisor && isPowerOfTwo(divisor));
return roundUpToMultipleOfImpl<T>(divisor, x);
}
template<size_t divisor>
constexpr size_t roundUpToMultipleOf(size_t x)
{
static_assert(divisor && isPowerOfTwo(divisor));
return roundUpToMultipleOfImpl(divisor, x);
}
template<size_t divisor, typename T>
constexpr T* roundUpToMultipleOf(T* x)
{
static_assert(sizeof(T*) == sizeof(size_t));
return reinterpret_cast<T*>(roundUpToMultipleOf<divisor>(reinterpret_cast<size_t>(x)));
}
template<typename T>
constexpr T roundUpToMultipleOfNonPowerOfTwo(size_t divisor, T x)
{
T remainder = x % divisor;
if (!remainder)
return x;
return x + static_cast<T>(divisor - remainder);
}
template<typename T, typename C>
constexpr Checked<T, C> roundUpToMultipleOfNonPowerOfTwo(Checked<T, C> divisor, Checked<T, C> x)
{
if (x.hasOverflowed() || divisor.hasOverflowed())
return ResultOverflowed;
T remainder = x % divisor;
if (!remainder)
return x;
return x + static_cast<T>(divisor.value() - remainder);
}
// Returns positive distance to next multiple of a power-of-two divisor.
template<size_t divisor>
constexpr size_t distanceToMultipleOf(size_t x)
{
static_assert(divisor && isPowerOfTwo(divisor));
return (divisor - (x % divisor)) % divisor;
}
template<typename T>
constexpr T roundDownToMultipleOf(size_t divisor, T x)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(divisor && isPowerOfTwo(divisor));
static_assert(sizeof(T) == sizeof(uintptr_t), "sizeof(T) must be equal to sizeof(uintptr_t).");
return static_cast<T>(mask(static_cast<uintptr_t>(x), ~(divisor - 1ul)));
}
template<typename T>
constexpr T* roundDownToMultipleOf(size_t divisor, T* x)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(isPowerOfTwo(divisor));
return reinterpret_cast<T*>(mask(reinterpret_cast<uintptr_t>(x), ~(divisor - 1ul)));
}
template<size_t divisor, typename T>
constexpr T roundDownToMultipleOf(T x)
{
static_assert(isPowerOfTwo(divisor), "'divisor' must be a power of two.");
return roundDownToMultipleOf(divisor, x);
}
// The following truncation helpers perform a direct hardware truncation of
// floating-point values to integer types. Unlike ECMAScript ToInt32/ToInt64
// (modular wrap-around), these produce architecture-defined results for
// out-of-range inputs (e.g., NaN, infinity, values exceeding the target
// range). They are intended for call sites that check the result after
// conversion (e.g., round-trip comparison) or that are fine with a
// deterministic but unspecified value on overflow. The inline-asm paths avoid
// C++ undefined behaviour that a plain static_cast would trigger for
// non-representable values.
// Double-to-integer truncation helpers.
#define WTF_PROVEN_TRUE(x) (__builtin_constant_p(x) && (x))
SUPPRESS_NODELETE ALWAYS_INLINE int32_t NODELETE truncateDoubleToInt32(double number)
{
#if CPU(X86_64)
return _mm_cvttsd_si32(_mm_set_sd(number));
#elif CPU(ARM64)
if (WTF_PROVEN_TRUE(number > -2147483649.0 && number < 2147483648.0))
return static_cast<int32_t>(number);
// fcvtzs w0, d0
int32_t result;
__asm__("fcvtzs %w0, %d1" : "=r"(result) : "w"(number));
return result;
#else
if (WTF_PROVEN_TRUE(number > -2147483649.0 && number < 2147483648.0))
return static_cast<int32_t>(number);
if (!std::isfinite(number))
return 0;
if (number > 0) {
if (number >= static_cast<double>(INT32_MAX) + 1.0)
return INT32_MIN; // Saturate/wrap matching cvttsd2si overflow sentinel.
return static_cast<int32_t>(number);
}
if (number < static_cast<double>(INT32_MIN))
return INT32_MIN;
return static_cast<int32_t>(number);
#endif
}
SUPPRESS_NODELETE ALWAYS_INLINE int64_t NODELETE truncateDoubleToInt64(double number)
{
#if CPU(X86_64)
return _mm_cvttsd_si64(_mm_set_sd(number));
#elif CPU(ARM64)
return vcvtd_s64_f64(number);
#else
if (WTF_PROVEN_TRUE(number >= -9223372036854775808.0 && number < 9223372036854775808.0))
return static_cast<int64_t>(number);
if (!std::isfinite(number))
return 0;
if (number > 0) {
if (number >= static_cast<double>(INT64_MAX) + 1.0)
return INT64_MIN; // Saturate/wrap matching cvttsd2si overflow sentinel.
return static_cast<int64_t>(number);
}
if (number < static_cast<double>(INT64_MIN))
return INT64_MIN;
return static_cast<int64_t>(number);
#endif
}
SUPPRESS_NODELETE ALWAYS_INLINE uint32_t NODELETE truncateDoubleToUint32(double number)
{
#if CPU(X86_64)
return static_cast<uint32_t>(_mm_cvttsd_si64(_mm_set_sd(number)));
#elif CPU(ARM64)
if (WTF_PROVEN_TRUE(number >= 0.0 && number < 4294967296.0))
return static_cast<uint32_t>(number);
// fcvtzu w0, d0
uint32_t result;
__asm__("fcvtzu %w0, %d1" : "=r"(result) : "w"(number));
return result;
#else
if (WTF_PROVEN_TRUE(number >= 0.0 && number < 4294967296.0))
return static_cast<uint32_t>(number);
if (!std::isfinite(number))
return 0;
// Mimic x86_64: cvttsd2si into int64, take low 32 bits.
int64_t wide = truncateDoubleToInt64(number);
return static_cast<uint32_t>(wide);
#endif
}
SUPPRESS_NODELETE ALWAYS_INLINE uint64_t NODELETE truncateDoubleToUint64(double number)
{
#if CPU(X86_64)
// Branchless conversion matching compiler codegen for static_cast<uint64_t>(double).
// cvttsd2si returns 0x8000000000000000 (negative) on overflow, including for
// values >= 2^63. When that happens, subtract 2^63 and convert again; the
// arithmetic-right-shift mask selects the adjusted result only on overflow.
constexpr double twoTo63 = 9223372036854775808.0; // 0x43e0000000000000
int64_t direct = _mm_cvttsd_si64(_mm_set_sd(number));
double shifted = number - twoTo63;
int64_t fromShifted = _mm_cvttsd_si64(_mm_set_sd(shifted));
int64_t mask = direct >> 63;
return static_cast<uint64_t>((fromShifted & mask) | direct);
#elif CPU(ARM64)
return vcvtd_u64_f64(number);
#else
if (WTF_PROVEN_TRUE(number >= 0.0 && number < 18446744073709551616.0))
return static_cast<uint64_t>(number);
if (!std::isfinite(number))
return 0;
if (number < 0.0)
return 0;
if (number >= 18446744073709551616.0)
return UINT64_MAX;
// For values >= 2^63, split into high and low halves to avoid UB.
constexpr double twoTo63 = 9223372036854775808.0;
if (number >= twoTo63) {
int64_t lo = static_cast<int64_t>(number - twoTo63);
return static_cast<uint64_t>(lo) + static_cast<uint64_t>(twoTo63);
}
return static_cast<uint64_t>(static_cast<int64_t>(number));
#endif
}
// Float-to-integer truncation helpers.
SUPPRESS_NODELETE ALWAYS_INLINE int32_t NODELETE truncateFloatToInt32(float number)
{
#if CPU(X86_64)
return _mm_cvttss_si32(_mm_set_ss(number));
#elif CPU(ARM64)
return vcvts_s32_f32(number);
#else
if (WTF_PROVEN_TRUE(number > -2147483649.0f && number < 2147483648.0f))
return static_cast<int32_t>(number);
if (!std::isfinite(number))
return 0;
if (number > 0) {
if (number >= static_cast<float>(INT32_MAX) + 1.0f)
return INT32_MIN;
return static_cast<int32_t>(number);
}
if (number < static_cast<float>(INT32_MIN))
return INT32_MIN;
return static_cast<int32_t>(number);
#endif
}
SUPPRESS_NODELETE ALWAYS_INLINE int64_t NODELETE truncateFloatToInt64(float number)
{
#if CPU(X86_64)
return _mm_cvttss_si64(_mm_set_ss(number));
#elif CPU(ARM64)
if (WTF_PROVEN_TRUE(number >= -9223372036854775808.0f && number < 9223372036854775808.0f))
return static_cast<int64_t>(number);
// fcvtzs x0, s0
int64_t result;
__asm__("fcvtzs %x0, %s1" : "=r"(result) : "w"(number));
return result;
#else
if (WTF_PROVEN_TRUE(number >= -9223372036854775808.0f && number < 9223372036854775808.0f))
return static_cast<int64_t>(number);
if (!std::isfinite(number))
return 0;
if (number > 0) {
if (number >= static_cast<float>(INT64_MAX) + 1.0f)
return INT64_MIN;
return static_cast<int64_t>(number);
}
if (number < static_cast<float>(INT64_MIN))
return INT64_MIN;
return static_cast<int64_t>(number);
#endif
}
SUPPRESS_NODELETE ALWAYS_INLINE uint32_t NODELETE truncateFloatToUint32(float number)
{
#if CPU(X86_64)
return static_cast<uint32_t>(_mm_cvttss_si64(_mm_set_ss(number)));
#elif CPU(ARM64)
return vcvts_u32_f32(number);
#else
if (WTF_PROVEN_TRUE(number >= 0.0f && number < 4294967296.0f))
return static_cast<uint32_t>(number);
if (!std::isfinite(number))
return 0;
int64_t wide = truncateFloatToInt64(number);
return static_cast<uint32_t>(wide);
#endif
}
SUPPRESS_NODELETE ALWAYS_INLINE uint64_t NODELETE truncateFloatToUint64(float number)
{
#if CPU(X86_64)
// Branchless conversion matching compiler codegen for static_cast<uint64_t>(float).
constexpr float twoTo63 = 9223372036854775808.0f; // 0x5f000000
int64_t direct = _mm_cvttss_si64(_mm_set_ss(number));
float shifted = number - twoTo63;
int64_t fromShifted = _mm_cvttss_si64(_mm_set_ss(shifted));
int64_t mask = direct >> 63;
return static_cast<uint64_t>((fromShifted & mask) | direct);
#elif CPU(ARM64)
if (WTF_PROVEN_TRUE(number >= 0.0f && number < 18446744073709551616.0f))
return static_cast<uint64_t>(number);
// fcvtzu x0, s0
uint64_t result;
__asm__("fcvtzu %x0, %s1" : "=r"(result) : "w"(number));
return result;
#else
if (WTF_PROVEN_TRUE(number >= 0.0f && number < 18446744073709551616.0f))
return static_cast<uint64_t>(number);
if (!std::isfinite(number))
return 0;
if (number < 0.0f)
return 0;
if (number >= 18446744073709551616.0f)
return UINT64_MAX;
constexpr float twoTo63 = 9223372036854775808.0f;
if (number >= twoTo63) {
int64_t lo = static_cast<int64_t>(number - twoTo63);
return static_cast<uint64_t>(lo) + static_cast<uint64_t>(twoTo63);
}
return static_cast<uint64_t>(static_cast<int64_t>(number));
#endif
}
// tryConvertToStrictInt32: Attempts to convert a double to int32_t, returning
// std::nullopt if the value is not exactly representable as int32 (including
// -0.0, NaN, Infinity, non-integer values, and out-of-range values).
SUPPRESS_NODELETE ALWAYS_INLINE std::optional<int32_t> NODELETE tryConvertToStrictInt32(double value)
{
#if HAVE(FJCVTZS_INSTRUCTION)
int32_t result;
bool isExact;
// Unlike other conversions on ARM, fjcvtzs sets the zero flag when no rounding occurred.
__asm__(
"fjcvtzs %w0, %d2"
: "=r" (result), "=@cceq" (isExact)
: "w" (value)
: "cc");
if (isExact)
return result;
return std::nullopt;
#else
if (std::isinf(value) || std::isnan(value))
return std::nullopt;
// Note that -0.0 is not StrictInt32.
const int32_t asInt32 = truncateDoubleToInt32(value);
if (!(asInt32 != value || (!asInt32 && std::signbit(value))))
return asInt32;
return std::nullopt;
#endif
}
} // namespace WTF
using WTF::shuffleVector;
using WTF::clz;
using WTF::ctz;
using WTF::getLSBSet;
using WTF::getMSBSet;
using WTF::isNaNConstExpr;
using WTF::fabsConstExpr;
using WTF::reverseBits32;
using WTF::roundDownToMultipleOf;
using WTF::roundUpToMultipleOf;
using WTF::roundUpToMultipleOfNonPowerOfTwo;
using WTF::distanceToMultipleOf;
using WTF::roundUpToPowerOfTwo;
using WTF::isIdentical;
using WTF::isRepresentableAs;
using WTF::isPowerOfTwo;
using WTF::truncateDoubleToInt32;
using WTF::truncateDoubleToInt64;
using WTF::truncateDoubleToUint32;
using WTF::truncateDoubleToUint64;
using WTF::truncateFloatToInt32;
using WTF::truncateFloatToInt64;
using WTF::truncateFloatToUint32;
using WTF::truncateFloatToUint64;
using WTF::tryConvertToStrictInt32;