From 8806668465bfa7f8fea8f4cf94aca10e535832b1 Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Fri, 11 Apr 2025 10:42:09 -0700 Subject: [PATCH 01/13] merge in folly 2024.10.14.00 --- vnext/Directory.Build.props | 4 +- vnext/Folly/Folly.vcxproj | 1 + .../TEMP_UntilFollyUpdate/ConstexprMath.h | 1886 +++++------ .../Folly/TEMP_UntilFollyUpdate/dynamic-inl.h | 2767 ++++++++--------- vnext/Folly/TEMP_UntilFollyUpdate/json.cpp | 2191 ++++++------- .../TEMP_UntilFollyUpdate/lang/SafeAssert.h | 333 +- .../TEMP_UntilFollyUpdate/lang/ToAscii.h | 10 +- vnext/Folly/cgmanifest.json | 2 +- .../packages.lock.json | 73 +- 9 files changed, 3602 insertions(+), 3665 deletions(-) diff --git a/vnext/Directory.Build.props b/vnext/Directory.Build.props index 51977c2e25c..eede3d0724b 100644 --- a/vnext/Directory.Build.props +++ b/vnext/Directory.Build.props @@ -20,8 +20,8 @@ true false - 2024.01.01.00 - 234d39a36a43106747d10cc19efada72fd810dd3 + 2024.10.14.00 + ad90720829db5ba0c3d0e44994856dcce33d7940 10.1.0 ca2e3685b160617d3d95fcd9e789c4e06ca88 diff --git a/vnext/Folly/Folly.vcxproj b/vnext/Folly/Folly.vcxproj index ab9d5344d55..9c6c9f06d78 100644 --- a/vnext/Folly/Folly.vcxproj +++ b/vnext/Folly/Folly.vcxproj @@ -219,6 +219,7 @@ + diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h b/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h index 707eb604e8f..3eeacc95f8b 100644 --- a/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h +++ b/vnext/Folly/TEMP_UntilFollyUpdate/ConstexprMath.h @@ -14,247 +14,239 @@ * limitations under the License. */ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -namespace folly { - -/// numbers -/// -/// mimic: std::numbers, C++20 (partial) -namespace numbers { - -namespace detail { -template -using enable_if_floating_t = - std::enable_if_t::value, T>; -} - -/// e_v -/// -/// mimic: std::numbers::e_v, C++20 -template -FOLLY_INLINE_VARIABLE constexpr T e_v = detail::enable_if_floating_t( - 2.71828182845904523536028747135266249775724709369995L); - -/// ln2_v -/// -/// mimic: std::numbers::ln2_v, C++20 -template -FOLLY_INLINE_VARIABLE constexpr T ln2_v = detail::enable_if_floating_t( - 0.69314718055994530941723212145817656807550013436025L); - -/// e -/// -/// mimic: std::numbers::e, C++20 -FOLLY_INLINE_VARIABLE constexpr double e = e_v; - -/// ln2 -/// -/// mimic: std::numbers::ln2, C++20 -FOLLY_INLINE_VARIABLE constexpr double ln2 = ln2_v; - -} // namespace numbers - -/// floating_point_integral_constant -/// -/// Like std::integral_constant but for floating-point types holding integral -/// values representable in an integral type. -template -struct floating_point_integral_constant { - using value_type = T; - static constexpr value_type value = static_cast(Value); - constexpr operator value_type() const noexcept { return value; } - constexpr value_type operator()() const noexcept { return value; } -}; -#if FOLLY_CPLUSPLUS < 201703L -template -constexpr typename floating_point_integral_constant::value_type - floating_point_integral_constant::value; -#endif - -// ---- - -namespace detail { - -template -constexpr size_t constexpr_iterated_squares_desc_size_(T const base) { - using lim = std::numeric_limits; - size_t s = 1; - auto r = base; - while (r <= lim::max() / r) { - ++s; - r *= r; - } - return s; -} - -} // namespace detail - -/// constexpr_iterated_squares_desc_size_v -/// -/// Effectively calculates: floor(log(max_exponent)/log(base)) -/// -/// For use with constexpr_iterated_squares_desc below. -template -FOLLY_INLINE_VARIABLE constexpr size_t constexpr_iterated_squares_desc_size_v = - detail::constexpr_iterated_squares_desc_size_(Base::value); - -/// constexpr_iterated_squares_desc -/// -/// A constexpr scaling array of integer powers-of-powers-of-two, descending, -/// with the associated powers-of-two. -/// -/// scaling = [..., {8, b^8}, {4, b^4}, {2, b^2}, {1, b^1}] for b = base -/// -/// Includes select constexpr scaling algorithms based on the scaling array. -/// -/// The scaling array and the scaling algorithms are general-purpose, if niche. -/// They may be used by other constexpr math functions (floating-point) either -/// to improve runtime performance or to improve numerical approximations. -/// -/// Some compilers fail to support passing some types as non-type template -/// params. In particular, long double is not universally supported. Therefore, -/// this utility takes its base as a type rather than as a value. For floating- -/// point integral bases, that is, bases of floating-point type but of integral -/// value, floating_point_integral_constant is the easiest parameterization. -template -struct constexpr_iterated_squares_desc { - static_assert(Size > 0, "requires non-zero size"); - - using size_type = decltype(Size); - using base_type = T; - - struct item_type { - size_type power; - base_type scale; - }; - - static constexpr size_type size = Size; - base_type base; - item_type scaling[size]; - - private: - using lim = std::numeric_limits; - - static_assert( - lim::max_exponent < std::numeric_limits::max(), - "size_type too small for base_type"); - - public: - explicit constexpr constexpr_iterated_squares_desc(base_type r) noexcept - : base{r}, scaling{} { - assert(size <= detail::constexpr_iterated_squares_desc_size_(base)); - size_type i = 0; - size_type p = 1; - while (true) { // a for-loop might cause multiplication overflow below - scaling[size - 1 - i] = {p, r}; - if (++i == size) { - break; - } - p *= 2; - r *= r; - } - } - - /// shrink - /// - /// Returns scaling params of the form: - /// item_type{power, scale} with scale = base ^ power - /// With power the smallest nonnegative integer such that: - /// abs(num) / scale <= max - constexpr item_type shrink(base_type const num, base_type const max) const { - assert(max > base_type(0)); - auto const rmax = max / base; - auto const snum = num < base_type(0) ? -num : num; - auto power = size_type(0); - auto scale = base_type(1); - if (!(snum / scale <= max)) { - for (auto const& i : scaling) { - auto const next = scale * i.scale; - auto const div = snum / next; - if (div <= rmax) { - continue; - } - power += i.power; - scale = next; - if (div <= max) { - break; - } - } - } - assert(snum / scale <= max); - return {power, scale}; - } - - /// growth - /// - /// Returns scaling params of the form: - /// item_type{power, scale} with scale = base ^ power - /// With power the smallest nonnegative integer such that: - /// abs(num) * scale >= min - constexpr item_type growth(base_type const num, base_type const min) const { - assert(min > base_type(0)); - auto const rmin = min * base; - auto const snum = num < base_type(0) ? -num : num; - auto power = size_type(0); - auto scale = base_type(1); - if (!(snum * scale >= min)) { - for (auto const& i : scaling) { - auto const next = scale * i.scale; - auto const mul = snum * next; - if (mul >= rmin) { - continue; - } - power += i.power; - scale = next; - if (mul >= min) { - break; - } - } - } - assert(snum * scale >= min); - return {power, scale}; - } -}; -#if FOLLY_CPLUSPLUS < 201703L -template -constexpr typename constexpr_iterated_squares_desc::size_type - constexpr_iterated_squares_desc::size; -#endif - -/// constexpr_iterated_squares_desc_v -/// -/// An instance of constexpr_iterated_squares_desc of max size with the given -/// base. -template -FOLLY_INLINE_VARIABLE constexpr auto constexpr_iterated_squares_desc_v = - constexpr_iterated_squares_desc< - typename Base::value_type, - constexpr_iterated_squares_desc_size_v>{Base::value}; - -/// constexpr_iterated_squares_desc_2_v -/// -/// An alias for constexpr_iterated_squares_desc_v with base 2, which is the -/// most common base to use with iterated-squares. -template -constexpr auto& constexpr_iterated_squares_desc_2_v = - constexpr_iterated_squares_desc_v< - floating_point_integral_constant>; - -// TLDR: Prefer using operator< for ordering. And when -// a and b are equivalent objects, we return b to make -// sorting stable. -// See http://stepanovpapers.com/notes.pdf for details. -template + #pragma once + + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + namespace folly { + + /// numbers + /// + /// mimic: std::numbers, C++20 (partial) + namespace numbers { + + namespace detail { + template + using enable_if_floating_t = + std::enable_if_t::value, T>; + } + + /// e_v + /// + /// mimic: std::numbers::e_v, C++20 + template + inline constexpr T e_v = detail::enable_if_floating_t( + 2.71828182845904523536028747135266249775724709369995L); + + /// ln2_v + /// + /// mimic: std::numbers::ln2_v, C++20 + template + inline constexpr T ln2_v = detail::enable_if_floating_t( + 0.69314718055994530941723212145817656807550013436025L); + + /// e + /// + /// mimic: std::numbers::e, C++20 + inline constexpr double e = e_v; + + /// ln2 + /// + /// mimic: std::numbers::ln2, C++20 + inline constexpr double ln2 = ln2_v; + + } // namespace numbers + + /// floating_point_integral_constant + /// + /// Like std::integral_constant but for floating-point types holding integral + /// values representable in an integral type. + template + struct floating_point_integral_constant { + using value_type = T; + static constexpr value_type value = static_cast(Value); + constexpr operator value_type() const noexcept { return value; } + constexpr value_type operator()() const noexcept { return value; } + }; + + // ---- + + namespace detail { + + template + constexpr size_t constexpr_iterated_squares_desc_size_(T const base) { + using lim = std::numeric_limits; + size_t s = 1; + auto r = base; + while (r <= lim::max() / r) { + ++s; + r *= r; + } + return s; + } + + } // namespace detail + + /// constexpr_iterated_squares_desc_size_v + /// + /// Effectively calculates: floor(log(max_exponent)/log(base)) + /// + /// For use with constexpr_iterated_squares_desc below. + template + inline constexpr size_t constexpr_iterated_squares_desc_size_v = + detail::constexpr_iterated_squares_desc_size_(Base::value); + + /// constexpr_iterated_squares_desc + /// + /// A constexpr scaling array of integer powers-of-powers-of-two, descending, + /// with the associated powers-of-two. + /// + /// scaling = [..., {8, b^8}, {4, b^4}, {2, b^2}, {1, b^1}] for b = base + /// + /// Includes select constexpr scaling algorithms based on the scaling array. + /// + /// The scaling array and the scaling algorithms are general-purpose, if niche. + /// They may be used by other constexpr math functions (floating-point) either + /// to improve runtime performance or to improve numerical approximations. + /// + /// Some compilers fail to support passing some types as non-type template + /// params. In particular, long double is not universally supported. Therefore, + /// this utility takes its base as a type rather than as a value. For floating- + /// point integral bases, that is, bases of floating-point type but of integral + /// value, floating_point_integral_constant is the easiest parameterization. + template + struct constexpr_iterated_squares_desc { + static_assert(Size > 0, "requires non-zero size"); + + using size_type = decltype(Size); + using base_type = T; + + struct item_type { + size_type power; + base_type scale; + }; + + static constexpr size_type size = Size; + base_type base; + item_type scaling[size]; + + private: + using lim = std::numeric_limits; + + static_assert( + lim::max_exponent < std::numeric_limits::max(), + "size_type too small for base_type"); + + public: + explicit constexpr constexpr_iterated_squares_desc(base_type r) noexcept + : base{r}, scaling{} { + assert(size <= detail::constexpr_iterated_squares_desc_size_(base)); + size_type i = 0; + size_type p = 1; + while (true) { // a for-loop might cause multiplication overflow below + scaling[size - 1 - i] = {p, r}; + if (++i == size) { + break; + } + p *= 2; + r *= r; + } + } + + /// shrink + /// + /// Returns scaling params of the form: + /// item_type{power, scale} with scale = base ^ power + /// With power the smallest nonnegative integer such that: + /// abs(num) / scale <= max + constexpr item_type shrink(base_type const num, base_type const max) const { + assert(max > base_type(0)); + auto const rmax = max / base; + auto const snum = num < base_type(0) ? -num : num; + auto power = size_type(0); + auto scale = base_type(1); + if (!(snum / scale <= max)) { + for (auto const& i : scaling) { + auto const next = scale * i.scale; + auto const div = snum / next; + if (div <= rmax) { + continue; + } + power += i.power; + scale = next; + if (div <= max) { + break; + } + } + } + assert(snum / scale <= max); + return {power, scale}; + } + + /// growth + /// + /// Returns scaling params of the form: + /// item_type{power, scale} with scale = base ^ power + /// With power the smallest nonnegative integer such that: + /// abs(num) * scale >= min + constexpr item_type growth(base_type const num, base_type const min) const { + assert(min > base_type(0)); + auto const rmin = min * base; + auto const snum = num < base_type(0) ? -num : num; + auto power = size_type(0); + auto scale = base_type(1); + if (!(snum * scale >= min)) { + for (auto const& i : scaling) { + auto const next = scale * i.scale; + auto const mul = snum * next; + if (mul >= rmin) { + continue; + } + power += i.power; + scale = next; + if (mul >= min) { + break; + } + } + } + assert(snum * scale >= min); + return {power, scale}; + } + }; + + /// constexpr_iterated_squares_desc_v + /// + /// An instance of constexpr_iterated_squares_desc of max size with the given + /// base. + template + inline constexpr auto constexpr_iterated_squares_desc_v = + constexpr_iterated_squares_desc< + typename Base::value_type, + constexpr_iterated_squares_desc_size_v>{Base::value}; + + /// constexpr_iterated_squares_desc_2_v + /// + /// An alias for constexpr_iterated_squares_desc_v with base 2, which is the + /// most common base to use with iterated-squares. + template + constexpr auto& constexpr_iterated_squares_desc_2_v = + constexpr_iterated_squares_desc_v< + floating_point_integral_constant>; + + // TLDR: Prefer using operator< for ordering. And when + // a and b are equivalent objects, we return b to make + // sorting stable. + // See http://stepanovpapers.com/notes.pdf for details. + template constexpr T constexpr_max(T a, Ts... ts) { T list[] = {ts..., a}; // 0-length arrays are illegal // [Windows #12703 - Fix folly CodeQL issues] @@ -263,708 +255,724 @@ constexpr T constexpr_max(T a, Ts... ts) { } return a; } - -// When a and b are equivalent objects, we return a to -// make sorting stable. -template -constexpr T constexpr_min(T a, Ts... ts) { + + // When a and b are equivalent objects, we return a to + // make sorting stable. + template + constexpr T constexpr_min(T a, Ts... ts) { T list[] = {ts..., a}; // 0-length arrays are illegal // [Windows #12703 - Fix folly CodeQL issues] for (size_t i = 0; i < sizeof...(Ts); ++i) { a = list[i] < a ? list[i] : a; } return a; -} - -template -constexpr T const& constexpr_clamp( - T const& v, T const& lo, T const& hi, Less less) { - T const& a = less(v, lo) ? lo : v; - T const& b = less(hi, a) ? hi : a; - return b; -} -template -constexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) { - return constexpr_clamp(v, lo, hi, std::less{}); -} - -template -constexpr bool constexpr_isnan(T const t) { - return t != t; // NOLINT -} - -namespace detail { - -template -struct constexpr_abs_helper {}; - -template -struct constexpr_abs_helper< - T, - typename std::enable_if::value>::type> { - static constexpr T go(T t) { return t < static_cast(0) ? -t : t; } -}; - -template -struct constexpr_abs_helper< - T, - typename std::enable_if< - std::is_integral::value && !std::is_same::value && - std::is_unsigned::value>::type> { - static constexpr T go(T t) { return t; } -}; - -template -struct constexpr_abs_helper< - T, - typename std::enable_if< - std::is_integral::value && !std::is_same::value && - std::is_signed::value>::type> { - static constexpr typename std::make_unsigned::type go(T t) { - return typename std::make_unsigned::type(t < static_cast(0) ? -t : t); - } -}; - -} // namespace detail - -template -constexpr auto constexpr_abs(T t) - -> decltype(detail::constexpr_abs_helper::go(t)) { - return detail::constexpr_abs_helper::go(t); -} - -namespace detail { - -template -constexpr T constexpr_log2_(T a, T e) { - return e == T(1) ? a : constexpr_log2_(a + T(1), e / T(2)); -} - -template -constexpr T constexpr_log2_ceil_(T l2, T t) { - return l2 + T(T(1) << l2 < t ? 1 : 0); -} - -} // namespace detail - -template -constexpr T constexpr_log2(T t) { - return detail::constexpr_log2_(T(0), t); -} - -template -constexpr T constexpr_log2_ceil(T t) { - return detail::constexpr_log2_ceil_(constexpr_log2(t), t); -} - -/// constexpr_trunc -/// -/// mimic: std::trunc (C++23) -template < - typename T, - std::enable_if_t::value, int> = 0> -constexpr T constexpr_trunc(T const t) { - using lim = std::numeric_limits; - using int_type = std::uintmax_t; - using int_lim = std::numeric_limits; - static_assert(lim::radix == 2, "non-binary radix"); - static_assert(lim::digits <= int_lim::digits, "overwide mantissa"); - constexpr auto bound = static_cast(std::uintmax_t(1) << (lim::digits - 1)); - auto const neg = !constexpr_isnan(t) && t < T(0); - auto const s = neg ? -t : t; - if (constexpr_isnan(t) || t == T(0) || !(s < bound)) { - return t; - } - if (s < T(1)) { - return neg ? -T(0) : T(0); - } - auto const r = static_cast(static_cast(s)); - return neg ? -r : r; -} - -template ::value, int> = 0> -constexpr T constexpr_trunc(T const t) { - return t; -} - -/// constexpr_round -/// -/// mimic: std::round (C++23) -template -constexpr T constexpr_round(T const t) { - constexpr auto half = T(1) / T(2); - auto const same = constexpr_isnan(t) || t == T(0); - return same ? t : constexpr_trunc(t < T(0) ? t - half : t + half); -} - -/// constexpr_floor -/// -/// mimic: std::floor (C++23) -template -constexpr T constexpr_floor(T const t) { - auto const s = constexpr_trunc(t); - return t < s ? s - T(1) : s; -} - -/// constexpr_ceil -/// -/// mimic: std::ceil (C++23) -template -constexpr T constexpr_ceil(T const t) { - auto const s = constexpr_trunc(t); - return s < t ? s + T(1) : s; -} - -/// constexpr_ceil -/// -/// The least integer at least t that round divides. -template -constexpr T constexpr_ceil(T t, T round) { - return round == T(0) - ? t - : ((t + (t < T(0) ? T(0) : round - T(1))) / round) * round; -} - -/// constexpr_mult -/// -/// Multiply two values, allowing for constexpr floating-pooint overflow to -/// infinity. -template -constexpr T constexpr_mult(T const a, T const b) { - using lim = std::numeric_limits; - if (constexpr_isnan(a) || constexpr_isnan(b)) { - return constexpr_isnan(a) ? a : b; - } - if (std::is_floating_point::value) { - constexpr auto inf = lim::infinity(); - auto const ax = constexpr_abs(a); - auto const bx = constexpr_abs(b); - if ((ax == T(0) && bx == inf) || (bx == T(0) && ax == inf)) { - return lim::quiet_NaN(); - } - // floating-point multiplication overflow, ie where multiplication of two - // finite values overflows to infinity of either sign, is not constexpr per - // gcc - // floating-point division overflow, ie where division of two finite values - // overflows to infinity of either sign, is not constexpr per gcc - // floating-point division by zero is not constexpr per any compiler, but we - // use it in the checks for the other two conditions - if (ax != inf && bx != inf && T(1) < bx && lim::max() / bx < ax) { - auto const a_neg = static_cast(a < T(0)); - auto const b_neg = static_cast(b < T(0)); - auto const sign = a_neg == b_neg ? T(1) : T(-1); - return sign * inf; - } - } - return a * b; -} - -namespace detail { - -template < - typename T, - typename E, - std::enable_if_t::value, int> = 1> -constexpr T constexpr_ipow(T const base, E const exp) { - if (std::is_floating_point::value) { - if (exp < E(0)) { - return T(1) / constexpr_ipow(base, -exp); - } - if (exp == E(0)) { - return T(1); - } - if (constexpr_isnan(base)) { - return base; - } - } - assert(!(exp < E(0)) && "negative exponent with integral base"); - if (exp == E(0)) { - return T(1); - } - if (exp == E(1)) { - return base; - } - auto const hexp = constexpr_trunc(exp / E(2)); - auto const div = constexpr_ipow(base, hexp); - auto const rem = hexp * E(2) == exp ? T(1) : base; - return constexpr_mult(constexpr_mult(div, div), rem); -} - -template < - typename T, - typename E, - std::enable_if_t::value, int> = 1> -constexpr T constexpr_ipow(T const base, E const exp) { - if (std::is_floating_point::value) { - if (exp == E(0)) { - return T(1); - } - if (constexpr_isnan(base)) { - return base; - } - } - if (exp == E(0)) { - return T(1); - } - if (exp == E(1)) { - return base; - } - auto const hexp = constexpr_trunc(exp / E(2)); - auto const div = constexpr_ipow(base, hexp); - auto const rem = hexp * E(2) == exp ? T(1) : base; - return constexpr_mult(constexpr_mult(div, div), rem); -} - -} // namespace detail - -/// constexpr_exp -/// -/// Calculates an approximation of the mathematical function exp(num). Usable in -/// constant evaluations. Like std::exp, which becomes constexpr in C++26. -/// -/// The integer overload uses iterated squaring and multiplication. The -/// floating-point overlaod naively evaluates the taylor series of exp(num) -/// until approximate convergence. -/// -/// mimic: std::exp (C++23, C++26) -template < - typename T, - typename N, - std::enable_if_t< - std::is_floating_point::value && std::is_integral::value && - !std::is_same::value, - int> = 0> -constexpr T constexpr_exp(N const power) { - auto const npower = constexpr_abs(power); - auto const result = detail::constexpr_ipow(numbers::e_v, npower); - return power < N(0) ? T(1) / result : result; -} -template < - typename N, - std::enable_if_t< - std::is_integral::value && !std::is_same::value, - int> = 0> -constexpr double constexpr_exp(N const power) { - return constexpr_exp(power); -} -template < - typename T, - std::enable_if_t::value, int> = 0> -constexpr T constexpr_exp(T const power) { - using lim = std::numeric_limits; - - // edge cases - if (constexpr_isnan(power)) { - return power; - } - if (power == -lim::infinity()) { - return +T(0); - } - if (power == +lim::infinity()) { - return power; - } - - // convergence works better with positive powers since signs do not alternate - auto const abspower = constexpr_abs(power); - // convergence must short-circuit when terms grow to floating-point infinity - auto const bound = T(1) < abspower ? lim::max() / abspower : lim::infinity(); - - // term #index = power * coeff - auto index = size_t(0); - auto term = T(1); - // result = sum of terms - auto result = T(1); - // sum the terms until ~convergence - while (!(constexpr_abs(term) < lim::epsilon())) { - if (bound < term) { - return power < T(0) ? T(0) : lim::infinity(); - } - index += 1; - term = term * abspower / index; - result += term; - } - return power < T(0) ? T(1) / result : result; -} - -/// constexpr_log -/// -/// Calculates an approximation of the natural logarithm ln(num). -/// -/// The implementation uses a quickly-converging, high-precision iterative -/// technique as described in: -/// https://en.wikipedia.org/wiki/Natural_logarithm#High_precision -/// -/// The technique works best with numbers that are close enough to 1, so the -/// implementation uses a quick shrink/growth technique as described in: -/// https://en.wikipedia.org/wiki/Natural_logarithm#Efficient_computation -template < - typename T, - std::enable_if_t::value, int> = 0> -constexpr T constexpr_log(T const num) { - using lim = std::numeric_limits; - constexpr auto& isq = constexpr_iterated_squares_desc_2_v; - - // edge cases - if (constexpr_isnan(num)) { - return num; - } - if (num < T(0)) { - return lim::quiet_NaN(); - } - if (num == T(0)) { - return -lim::infinity(); - } - if (num == lim::infinity()) { - return num; - } - - // compression - auto const shrink = isq.shrink(num, isq.base); - auto const growth = isq.growth(num, T(1)); - auto const scaled = num * growth.scale / shrink.scale; - assert(scaled <= isq.base); - assert(scaled >= T(1)); - - auto sum = T(0); - auto delta = T(2); - while (constexpr_abs(delta) >= lim::epsilon()) { - auto expterm = constexpr_exp(sum); - delta = T(2) * (scaled - expterm) / (scaled + expterm); - sum += delta; - } - auto const ln2 = numbers::ln2_v; - return sum - growth.power * ln2 + shrink.power * ln2; -} - -/// constexpr_pow -/// -/// Calculates an approximation of the value of base raised to the exponent exp. -/// -/// The implementation uses iterated squaring and multiplication for the integer -/// part of the exponent and uses the identity x^y = exp(y * log(x)) for the -/// fractional part of the exponent. -/// -/// Notes: -/// * Forbids base of +0 or -0 with finite non-positive exponent: in part since -/// the plausible infinite result would be sensitive to the sign of the zero; -/// and in part since std::pow would be required or permitted to raise error -/// div-by-zero. -/// * Forbids finite negative base with finite non-integer exponent: in part -/// since std::pow would be required to raise error invalid. -/// -/// mimic: std::pow (C++26) -template < - typename T, - typename E, - std::enable_if_t< - std::is_integral::value && !std::is_same::value, - int> = 0> -constexpr T constexpr_pow(T const base, E const exp) { - return detail::constexpr_ipow(base, exp); -} -template < - typename T, - std::enable_if_t::value, int> = 0> -constexpr T constexpr_pow(T const base, T const exp) { - using lim = std::numeric_limits; - - // edge cases - if (exp == T(0)) { - return T(1); - } - if (constexpr_isnan(base)) { - return base; - } - if (exp == lim::infinity() || exp == -lim::infinity()) { - auto const abase = constexpr_abs(base); - if (abase < T(1)) { - return exp == lim::infinity() ? T(0) : lim::infinity(); - } - if (T(1) < abase) { - return exp == lim::infinity() ? lim::infinity() : T(0); - } - return T(1); - } - if (base == T(1)) { - return base; - } - if (constexpr_isnan(exp)) { - return exp; - } - assert(base != T(0) || exp > T(0)); // error div-by-zero - if (base == lim::infinity()) { - return exp < T(0) ? T(0) : lim::infinity(); - } - if (base == -lim::infinity()) { - auto const oddi = // - exp == constexpr_trunc(exp) && - exp != constexpr_trunc(exp / T(2)) * T(2); - return (oddi ? -T(1) : T(1)) * (exp < T(0) ? T(0) : lim::infinity()); - } - if (base == T(0)) { - auto const oddi = // - exp == constexpr_trunc(exp) && - exp != constexpr_trunc(exp / T(2)) * T(2); - return oddi ? base : T(0); - } - if (exp < T(0)) { - return T(1) / constexpr_pow(base, -exp); - } - - // as an identity: x^y = exp(y * log(x)); but calculation is imprecise ... so, - // for better precision, split the calculation into its integral-power and its - // fractional-power components - // as a cost, the complexity of constexpr_ipow here is logarithmic in y, i.e., - // linear in the logarithm of y, which can be prohibitive - auto const exp_trunc = constexpr_trunc(exp); - assert(T(0) < base || exp == exp_trunc); // error invalid - auto const exp_fract = exp - exp_trunc; - auto const anyi = exp_fract == T(0); - return constexpr_mult( - detail::constexpr_ipow(base, exp_trunc), - anyi ? T(1) : constexpr_exp(exp_fract * constexpr_log(base))); -} - -/// constexpr_find_last_set -/// -/// Return the 1-based index of the most significant bit which is set. -/// For x > 0, constexpr_find_last_set(x) == 1 + floor(log2(x)). -template -constexpr std::size_t constexpr_find_last_set(T const t) { - using U = std::make_unsigned_t; - return t == T(0) ? 0 : 1 + constexpr_log2(static_cast(t)); -} - -namespace detail { -template -constexpr std::size_t constexpr_find_first_set_( - std::size_t s, std::size_t a, U const u) { - return s == 0 ? a - : constexpr_find_first_set_( - s / 2, a + s * bool((u >> a) % (U(1) << s) == U(0)), u); -} -} // namespace detail - -/// constexpr_find_first_set -/// -/// Return the 1-based index of the least significant bit which is set. -/// For x > 0, the exponent in the largest power of two which does not divide x. -template -constexpr std::size_t constexpr_find_first_set(T t) { - using U = std::make_unsigned_t; - using size = std::integral_constant; - return t == T(0) - ? 0 - : 1 + detail::constexpr_find_first_set_(size{}, 0, static_cast(t)); -} - -template -constexpr T constexpr_add_overflow_clamped(T a, T b) { - using L = std::numeric_limits; - using M = std::intmax_t; - static_assert( - !std::is_integral::value || sizeof(T) <= sizeof(M), - "Integral type too large!"); - // clang-format off - return - // don't do anything special for non-integral types. - !std::is_integral::value ? a + b : - // for narrow integral types, just convert to intmax_t. - sizeof(T) < sizeof(M) - ? T(constexpr_clamp( - static_cast(a) + static_cast(b), - static_cast(L::min()), - static_cast(L::max()))) : - // when a >= 0, cannot add more than `MAX - a` onto a. - !(a < 0) ? a + constexpr_min(b, T(L::max() - a)) : - // a < 0 && b >= 0, `a + b` will always be in valid range of type T. - !(b < 0) ? a + b : - // a < 0 && b < 0, keep the result >= MIN. + } + + template + constexpr T const& constexpr_clamp( + T const& v, T const& lo, T const& hi, Less less) { + T const& a = less(v, lo) ? lo : v; + T const& b = less(hi, a) ? hi : a; + return b; + } + template + constexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) { + return constexpr_clamp(v, lo, hi, std::less{}); + } + + template + constexpr bool constexpr_isnan(T const t) { + return t != t; // NOLINT + } + + namespace detail { + + template + struct constexpr_abs_helper {}; + + template + struct constexpr_abs_helper< + T, + typename std::enable_if::value>::type> { + static constexpr T go(T t) { return t < static_cast(0) ? -t : t; } + }; + + template + struct constexpr_abs_helper< + T, + typename std::enable_if< + std::is_integral::value && !std::is_same::value && + std::is_unsigned::value>::type> { + static constexpr T go(T t) { return t; } + }; + + template + struct constexpr_abs_helper< + T, + typename std::enable_if< + std::is_integral::value && !std::is_same::value && + std::is_signed::value>::type> { + static constexpr typename std::make_unsigned::type go(T t) { + return typename std::make_unsigned::type(t < static_cast(0) ? -t : t); + } + }; + + } // namespace detail + + template + constexpr auto constexpr_abs(T t) + -> decltype(detail::constexpr_abs_helper::go(t)) { + return detail::constexpr_abs_helper::go(t); + } + + namespace detail { + + template + constexpr T constexpr_log2_(T a, T e) { + return e == T(1) ? a : constexpr_log2_(a + T(1), e / T(2)); + } + + template + constexpr T constexpr_log2_ceil_(T l2, T t) { + return l2 + T(T(1) << l2 < t ? 1 : 0); + } + + } // namespace detail + + template + constexpr T constexpr_log2(T t) { + return detail::constexpr_log2_(T(0), t); + } + + template + constexpr T constexpr_log2_ceil(T t) { + return detail::constexpr_log2_ceil_(constexpr_log2(t), t); + } + + /// constexpr_trunc + /// + /// mimic: std::trunc (C++23) + template < + typename T, + std::enable_if_t::value, int> = 0> + constexpr T constexpr_trunc(T const t) { + using lim = std::numeric_limits; + using int_type = std::uintmax_t; + using int_lim = std::numeric_limits; + static_assert(lim::radix == 2, "non-binary radix"); + static_assert(lim::digits <= int_lim::digits, "overwide mantissa"); + constexpr auto bound = static_cast(std::uintmax_t(1) << (lim::digits - 1)); + auto const neg = !constexpr_isnan(t) && t < T(0); + auto const s = neg ? -t : t; + if (constexpr_isnan(t) || t == T(0) || !(s < bound)) { + return t; + } + if (s < T(1)) { + return neg ? -T(0) : T(0); + } + auto const r = static_cast(static_cast(s)); + return neg ? -r : r; + } + + template ::value, int> = 0> + constexpr T constexpr_trunc(T const t) { + return t; + } + + /// constexpr_round + /// + /// mimic: std::round (C++23) + template + constexpr T constexpr_round(T const t) { + constexpr auto half = T(1) / T(2); + auto const same = constexpr_isnan(t) || t == T(0); + return same ? t : constexpr_trunc(t < T(0) ? t - half : t + half); + } + + /// constexpr_floor + /// + /// mimic: std::floor (C++23) + template + constexpr T constexpr_floor(T const t) { + auto const s = constexpr_trunc(t); + return t < s ? s - T(1) : s; + } + + /// constexpr_ceil + /// + /// mimic: std::ceil (C++23) + template + constexpr T constexpr_ceil(T const t) { + auto const s = constexpr_trunc(t); + return s < t ? s + T(1) : s; + } + + /// constexpr_ceil + /// + /// The least integer at least t that round divides. + template + constexpr T constexpr_ceil(T t, T round) { + return round == T(0) + ? t + : ((t + (t <= T(0) ? T(0) : round - T(1))) / round) * round; + } + + /// constexpr_mult + /// + /// Multiply two values, allowing for constexpr floating-pooint overflow to + /// infinity. + template + constexpr T constexpr_mult(T const a, T const b) { + using lim = std::numeric_limits; + if (constexpr_isnan(a) || constexpr_isnan(b)) { + return constexpr_isnan(a) ? a : b; + } + if (std::is_floating_point::value) { + constexpr auto inf = lim::infinity(); + auto const ax = constexpr_abs(a); + auto const bx = constexpr_abs(b); + if ((ax == T(0) && bx == inf) || (bx == T(0) && ax == inf)) { + return lim::quiet_NaN(); + } + // floating-point multiplication overflow, ie where multiplication of two + // finite values overflows to infinity of either sign, is not constexpr per + // gcc + // floating-point division overflow, ie where division of two finite values + // overflows to infinity of either sign, is not constexpr per gcc + // floating-point division by zero is not constexpr per any compiler, but we + // use it in the checks for the other two conditions + if (ax != inf && bx != inf && T(1) < bx && lim::max() / bx < ax) { + auto const a_neg = static_cast(a < T(0)); + auto const b_neg = static_cast(b < T(0)); + auto const sign = a_neg == b_neg ? T(1) : T(-1); + return sign * inf; + } + } + return a * b; + } + + namespace detail { + + template < + typename T, + typename E, + std::enable_if_t::value, int> = 1> + constexpr T constexpr_ipow(T const base, E const exp) { + if (std::is_floating_point::value) { + if (exp < E(0)) { + return T(1) / constexpr_ipow(base, -exp); + } + if (exp == E(0)) { + return T(1); + } + if (constexpr_isnan(base)) { + return base; + } + } + assert(!(exp < E(0)) && "negative exponent with integral base"); + if (exp == E(0)) { + return T(1); + } + if (exp == E(1)) { + return base; + } + auto const hexp = constexpr_trunc(exp / E(2)); + auto const div = constexpr_ipow(base, hexp); + auto const rem = hexp * E(2) == exp ? T(1) : base; + return constexpr_mult(constexpr_mult(div, div), rem); + } + + template < + typename T, + typename E, + std::enable_if_t::value, int> = 1> + constexpr T constexpr_ipow(T const base, E const exp) { + if (std::is_floating_point::value) { + if (exp == E(0)) { + return T(1); + } + if (constexpr_isnan(base)) { + return base; + } + } + if (exp == E(0)) { + return T(1); + } + if (exp == E(1)) { + return base; + } + auto const hexp = constexpr_trunc(exp / E(2)); + auto const div = constexpr_ipow(base, hexp); + auto const rem = hexp * E(2) == exp ? T(1) : base; + return constexpr_mult(constexpr_mult(div, div), rem); + } + + } // namespace detail + + /// constexpr_exp + /// + /// Calculates an approximation of the mathematical function exp(num). Usable in + /// constant evaluations. Like std::exp, which becomes constexpr in C++26. + /// + /// The integer overload uses iterated squaring and multiplication. The + /// floating-point overlaod naively evaluates the taylor series of exp(num) + /// until approximate convergence. + /// + /// mimic: std::exp (C++23, C++26) + template < + typename T, + typename N, + std::enable_if_t< + std::is_floating_point::value && std::is_integral::value && + !std::is_same::value, + int> = 0> + constexpr T constexpr_exp(N const power) { + auto const npower = constexpr_abs(power); + auto const result = detail::constexpr_ipow(numbers::e_v, npower); + return power < N(0) ? T(1) / result : result; + } + template < + typename N, + std::enable_if_t< + std::is_integral::value && !std::is_same::value, + int> = 0> + constexpr double constexpr_exp(N const power) { + return constexpr_exp(power); + } + template < + typename T, + std::enable_if_t::value, int> = 0> + constexpr T constexpr_exp(T const power) { + using lim = std::numeric_limits; + + // edge cases + if (constexpr_isnan(power)) { + return power; + } + if (power == -lim::infinity()) { + return +T(0); + } + if (power == +lim::infinity()) { + return power; + } + + // convergence works better with positive powers since signs do not alternate + auto const abspower = constexpr_abs(power); + // convergence must short-circuit when terms grow to floating-point infinity + auto const bound = T(1) < abspower ? lim::max() / abspower : lim::infinity(); + + // term #index = power * coeff + auto index = size_t(0); + auto term = T(1); + // result = sum of terms + auto result = T(1); + // sum the terms until ~convergence + while (!(constexpr_abs(term) < lim::epsilon())) { + if (bound < term) { + return power < T(0) ? T(0) : lim::infinity(); + } + index += 1; + term = term * abspower / index; + result += term; + } + return power < T(0) ? T(1) / result : result; + } + + /// constexpr_log + /// + /// Calculates an approximation of the natural logarithm ln(num). + /// + /// The implementation uses a quickly-converging, high-precision iterative + /// technique as described in: + /// https://en.wikipedia.org/wiki/Natural_logarithm#High_precision + /// + /// The technique works best with numbers that are close enough to 1, so the + /// implementation uses a quick shrink/growth technique as described in: + /// https://en.wikipedia.org/wiki/Natural_logarithm#Efficient_computation + template < + typename T, + std::enable_if_t::value, int> = 0> + constexpr T constexpr_log(T const num) { + using lim = std::numeric_limits; + constexpr auto& isq = constexpr_iterated_squares_desc_2_v; + + // edge cases + if (constexpr_isnan(num)) { + return num; + } + if (num < T(0)) { + return lim::quiet_NaN(); + } + if (num == T(0)) { + return -lim::infinity(); + } + if (num == lim::infinity()) { + return num; + } + + // compression + auto const shrink = isq.shrink(num, isq.base); + auto const growth = isq.growth(num, T(1)); + auto const scaled = num * growth.scale / shrink.scale; + assert(scaled <= isq.base); + assert(scaled >= T(1)); + + auto sum = T(0); + auto delta = T(2); + while (constexpr_abs(delta) >= lim::epsilon()) { + auto expterm = constexpr_exp(sum); + delta = T(2) * (scaled - expterm) / (scaled + expterm); + sum += delta; + } + auto const ln2 = numbers::ln2_v; + return sum - growth.power * ln2 + shrink.power * ln2; + } + + /// constexpr_pow + /// + /// Calculates an approximation of the value of base raised to the exponent exp. + /// + /// The implementation uses iterated squaring and multiplication for the integer + /// part of the exponent and uses the identity x^y = exp(y * log(x)) for the + /// fractional part of the exponent. + /// + /// Notes: + /// * Forbids base of +0 or -0 with finite non-positive exponent: in part since + /// the plausible infinite result would be sensitive to the sign of the zero; + /// and in part since std::pow would be required or permitted to raise error + /// div-by-zero. + /// * Forbids finite negative base with finite non-integer exponent: in part + /// since std::pow would be required to raise error invalid. + /// + /// mimic: std::pow (C++26) + template < + typename T, + typename E, + std::enable_if_t< + std::is_integral::value && !std::is_same::value, + int> = 0> + constexpr T constexpr_pow(T const base, E const exp) { + return detail::constexpr_ipow(base, exp); + } + template < + typename T, + std::enable_if_t::value, int> = 0> + constexpr T constexpr_pow(T const base, T const exp) { + using lim = std::numeric_limits; + + // edge cases + if (exp == T(0)) { + return T(1); + } + if (constexpr_isnan(base)) { + return base; + } + if (exp == lim::infinity() || exp == -lim::infinity()) { + auto const abase = constexpr_abs(base); + if (abase < T(1)) { + return exp == lim::infinity() ? T(0) : lim::infinity(); + } + if (T(1) < abase) { + return exp == lim::infinity() ? lim::infinity() : T(0); + } + return T(1); + } + if (base == T(1)) { + return base; + } + if (constexpr_isnan(exp)) { + return exp; + } + assert(base != T(0) || exp > T(0)); // error div-by-zero + if (base == lim::infinity()) { + return exp < T(0) ? T(0) : lim::infinity(); + } + if (base == -lim::infinity()) { + auto const oddi = // + exp == constexpr_trunc(exp) && + exp != constexpr_trunc(exp / T(2)) * T(2); + return (oddi ? -T(1) : T(1)) * (exp < T(0) ? T(0) : lim::infinity()); + } + if (base == T(0)) { + auto const oddi = // + exp == constexpr_trunc(exp) && + exp != constexpr_trunc(exp / T(2)) * T(2); + return oddi ? base : T(0); + } + if (exp < T(0)) { + return T(1) / constexpr_pow(base, -exp); + } + + // as an identity: x^y = exp(y * log(x)); but calculation is imprecise ... so, + // for better precision, split the calculation into its integral-power and its + // fractional-power components + // as a cost, the complexity of constexpr_ipow here is logarithmic in y, i.e., + // linear in the logarithm of y, which can be prohibitive + auto const exp_trunc = constexpr_trunc(exp); + assert(T(0) < base || exp == exp_trunc); // error invalid + auto const exp_fract = exp - exp_trunc; + auto const anyi = exp_fract == T(0); + return constexpr_mult( + detail::constexpr_ipow(base, exp_trunc), + anyi ? T(1) : constexpr_exp(exp_fract * constexpr_log(base))); + } + + /// constexpr_find_last_set + /// + /// Return the 1-based index of the most significant bit which is set. + /// For x > 0, constexpr_find_last_set(x) == 1 + floor(log2(x)). + template + constexpr std::size_t constexpr_find_last_set(T const t) { + using U = std::make_unsigned_t; + return t == T(0) ? 0 : 1 + constexpr_log2(static_cast(t)); + } + + namespace detail { + template + constexpr std::size_t constexpr_find_first_set_( + std::size_t s, std::size_t a, U const u) { + return s == 0 ? a + : constexpr_find_first_set_( + s / 2, a + s * bool((u >> a) % (U(1) << s) == U(0)), u); + } + } // namespace detail + + /// constexpr_find_first_set + /// + /// Return the 1-based index of the least significant bit which is set. + /// For x > 0, the exponent in the largest power of two which does not divide x. + template + constexpr std::size_t constexpr_find_first_set(T t) { + using U = std::make_unsigned_t; + using size = std::integral_constant; + return t == T(0) + ? 0 + : 1 + detail::constexpr_find_first_set_(size{}, 0, static_cast(t)); + } + + template + constexpr T constexpr_add_overflow_clamped(T a, T b) { + using L = std::numeric_limits; + using M = std::intmax_t; + static_assert( + !std::is_integral::value || sizeof(T) <= sizeof(M), + "Integral type too large!"); + if (!folly::is_constant_evaluated_or(true)) { + if constexpr (std::is_integral_v) { + T ret{}; + if (FOLLY_UNLIKELY(!checked_add(&ret, a, b))) { + if constexpr (std::is_signed_v) { + // Could be either overflow or underflow for signed types. + // Can only be underflow if both inputs are negative. + if (a < 0 && b < 0) { + return L::min(); + } + } + return L::max(); + } + return ret; + } + } + // clang-format off + return + // don't do anything special for non-integral types. + !std::is_integral::value ? a + b : + // for narrow integral types, just convert to intmax_t. + sizeof(T) < sizeof(M) + ? T(constexpr_clamp( + static_cast(a) + static_cast(b), + static_cast(L::min()), + static_cast(L::max()))) : + // when a >= 0, cannot add more than `MAX - a` onto a. + !(a < 0) ? a + constexpr_min(b, T(L::max() - a)) : + // a < 0 && b >= 0, `a + b` will always be in valid range of type T. + !(b < 0) ? a + b : + // a < 0 && b < 0, keep the result >= MIN. a + constexpr_max(b, T(L::min() - a)); - // clang-format on -} - -template -constexpr T constexpr_sub_overflow_clamped(T a, T b) { - using L = std::numeric_limits; - using M = std::intmax_t; - static_assert( - !std::is_integral::value || sizeof(T) <= sizeof(M), - "Integral type too large!"); - // clang-format off - return - // don't do anything special for non-integral types. - !std::is_integral::value ? a - b : - // for unsigned type, keep result >= 0. - std::is_unsigned::value ? (a < b ? 0 : a - b) : - // for narrow signed integral types, just convert to intmax_t. - sizeof(T) < sizeof(M) - ? T(constexpr_clamp( - static_cast(a) - static_cast(b), - static_cast(L::min()), - static_cast(L::max()))) : - // (a >= 0 && b >= 0) || (a < 0 && b < 0), `a - b` will always be valid. - (a < 0) == (b < 0) ? a - b : - // MIN < b, so `-b` should be in valid range (-MAX <= -b <= MAX), - // convert subtraction to addition. - L::min() < b ? constexpr_add_overflow_clamped(a, T(-b)) : - // -b = -MIN = (MAX + 1) and a <= -1, result is in valid range. - a < 0 ? a - b : - // -b = -MIN = (MAX + 1) and a >= 0, result > MAX. - L::max(); - // clang-format on -} - -// clamp_cast<> provides sane numeric conversions from float point numbers to -// integral numbers, and between different types of integral numbers. It helps -// to avoid unexpected bugs introduced by bad conversion, and undefined behavior -// like overflow when casting float point numbers to integral numbers. -// -// When doing clamp_cast(value), if `value` is in valid range of Dst, -// it will give correct result in Dst, equal to `value`. -// -// If `value` is outside the representable range of Dst, it will be clamped to -// MAX or MIN in Dst, instead of being undefined behavior. -// -// Float NaNs are converted to 0 in integral type. -// -// Here's some comparison with static_cast<>: -// (with FB-internal gcc-5-glibc-2.23 toolchain) -// -// static_cast(NaN) = 6 -// clamp_cast(NaN) = 0 -// -// static_cast(9999999999.0f) = -348639895 -// clamp_cast(9999999999.0f) = 2147483647 -// -// static_cast(2147483647.0f) = -348639895 -// clamp_cast(2147483647.0f) = 2147483647 -// -// static_cast(4294967295.0f) = 0 -// clamp_cast(4294967295.0f) = 4294967295 -// -// static_cast(-1) = 4294967295 -// clamp_cast(-1) = 0 -// -// static_cast(32768u) = -32768 -// clamp_cast(32768u) = 32767 - -template -constexpr typename std::enable_if::value, Dst>::type -constexpr_clamp_cast(Src src) { - static_assert( - std::is_integral::value && sizeof(Dst) <= sizeof(int64_t), - "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); - - using L = std::numeric_limits; - // clang-format off - return - // Check if Src and Dst have same signedness. - std::is_signed::value == std::is_signed::value - ? ( - // Src and Dst have same signedness. If sizeof(Src) <= sizeof(Dst), - // we can safely convert Src to Dst without any loss of accuracy. - sizeof(Src) <= sizeof(Dst) ? Dst(src) : - // If Src is larger in size, we need to clamp it to valid range in Dst. - Dst(constexpr_clamp(src, Src(L::min()), Src(L::max())))) - // Src and Dst have different signedness. - // Check if it's signed -> unsigend cast. - : std::is_signed::value && std::is_unsigned::value - ? ( - // If src < 0, the result should be 0. - src < 0 ? Dst(0) : - // Otherwise, src >= 0. If src can fit into Dst, we can safely cast it - // without loss of accuracy. - sizeof(Src) <= sizeof(Dst) ? Dst(src) : - // If Src is larger in size than Dst, we need to ensure the result is - // at most Dst MAX. - Dst(constexpr_min(src, Src(L::max())))) - // It's unsigned -> signed cast. - : ( - // Since Src is unsigned, and Dst is signed, Src can fit into Dst only - // when sizeof(Src) < sizeof(Dst). - sizeof(Src) < sizeof(Dst) ? Dst(src) : - // If Src does not fit into Dst, we need to ensure the result is at most - // Dst MAX. - Dst(constexpr_min(src, Src(L::max())))); - // clang-format on -} - -namespace detail { -// Upper/lower bound values that could be accurately represented in both -// integral and float point types. -constexpr double kClampCastLowerBoundDoubleToInt64F = -9223372036854774784.0; -constexpr double kClampCastUpperBoundDoubleToInt64F = 9223372036854774784.0; -constexpr double kClampCastUpperBoundDoubleToUInt64F = 18446744073709549568.0; - -constexpr float kClampCastLowerBoundFloatToInt32F = -2147483520.0f; -constexpr float kClampCastUpperBoundFloatToInt32F = 2147483520.0f; -constexpr float kClampCastUpperBoundFloatToUInt32F = 4294967040.0f; - -// This works the same as constexpr_clamp, but the comparison are done in Src -// to prevent any implicit promotions. -template -constexpr D constexpr_clamp_cast_helper(S src, S sl, S su, D dl, D du) { - return src < sl ? dl : (src > su ? du : D(src)); -} -} // namespace detail - -template -constexpr typename std::enable_if::value, Dst>::type -constexpr_clamp_cast(Src src) { - static_assert( - std::is_integral::value && sizeof(Dst) <= sizeof(int64_t), - "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); - - using L = std::numeric_limits; - // clang-format off - return - // Special case: cast NaN into 0. - constexpr_isnan(src) ? Dst(0) : - // using `sizeof(Src) > sizeof(Dst)` as a heuristic that Dst can be - // represented in Src without loss of accuracy. - // see: https://en.wikipedia.org/wiki/Floating-point_arithmetic - sizeof(Src) > sizeof(Dst) ? - detail::constexpr_clamp_cast_helper( - src, Src(L::min()), Src(L::max()), L::min(), L::max()) : - // sizeof(Src) < sizeof(Dst) only happens when doing cast of - // 32bit float -> u/int64_t. - // Losslessly promote float into double, change into double -> u/int64_t. - sizeof(Src) < sizeof(Dst) ? ( - src >= 0.0 - ? constexpr_clamp_cast( - constexpr_clamp_cast(double(src))) - : constexpr_clamp_cast( - constexpr_clamp_cast(double(src)))) : - // The following are for sizeof(Src) == sizeof(Dst). - std::is_same::value && std::is_same::value ? - detail::constexpr_clamp_cast_helper( - double(src), - detail::kClampCastLowerBoundDoubleToInt64F, - detail::kClampCastUpperBoundDoubleToInt64F, - L::min(), - L::max()) : - std::is_same::value && std::is_same::value ? - detail::constexpr_clamp_cast_helper( - double(src), - 0.0, - detail::kClampCastUpperBoundDoubleToUInt64F, - L::min(), - L::max()) : - std::is_same::value && std::is_same::value ? - detail::constexpr_clamp_cast_helper( - float(src), - detail::kClampCastLowerBoundFloatToInt32F, - detail::kClampCastUpperBoundFloatToInt32F, - L::min(), - L::max()) : - detail::constexpr_clamp_cast_helper( - float(src), - 0.0f, - detail::kClampCastUpperBoundFloatToUInt32F, - L::min(), - L::max()); - // clang-format on -} - -} // namespace folly + // clang-format on + } + + template + constexpr T constexpr_sub_overflow_clamped(T a, T b) { + using L = std::numeric_limits; + using M = std::intmax_t; + static_assert( + !std::is_integral::value || sizeof(T) <= sizeof(M), + "Integral type too large!"); + // clang-format off + return + // don't do anything special for non-integral types. + !std::is_integral::value ? a - b : + // for unsigned type, keep result >= 0. + std::is_unsigned::value ? (a < b ? 0 : a - b) : + // for narrow signed integral types, just convert to intmax_t. + sizeof(T) < sizeof(M) + ? T(constexpr_clamp( + static_cast(a) - static_cast(b), + static_cast(L::min()), + static_cast(L::max()))) : + // (a >= 0 && b >= 0) || (a < 0 && b < 0), `a - b` will always be valid. + (a < 0) == (b < 0) ? a - b : + // MIN < b, so `-b` should be in valid range (-MAX <= -b <= MAX), + // convert subtraction to addition. + L::min() < b ? constexpr_add_overflow_clamped(a, T(-b)) : + // -b = -MIN = (MAX + 1) and a <= -1, result is in valid range. + a < 0 ? a - b : + // -b = -MIN = (MAX + 1) and a >= 0, result > MAX. + L::max(); + // clang-format on + } + + // clamp_cast<> provides sane numeric conversions from float point numbers to + // integral numbers, and between different types of integral numbers. It helps + // to avoid unexpected bugs introduced by bad conversion, and undefined behavior + // like overflow when casting float point numbers to integral numbers. + // + // When doing clamp_cast(value), if `value` is in valid range of Dst, + // it will give correct result in Dst, equal to `value`. + // + // If `value` is outside the representable range of Dst, it will be clamped to + // MAX or MIN in Dst, instead of being undefined behavior. + // + // Float NaNs are converted to 0 in integral type. + // + // Here's some comparison with static_cast<>: + // (with FB-internal gcc-5-glibc-2.23 toolchain) + // + // static_cast(NaN) = 6 + // clamp_cast(NaN) = 0 + // + // static_cast(9999999999.0f) = -348639895 + // clamp_cast(9999999999.0f) = 2147483647 + // + // static_cast(2147483647.0f) = -348639895 + // clamp_cast(2147483647.0f) = 2147483647 + // + // static_cast(4294967295.0f) = 0 + // clamp_cast(4294967295.0f) = 4294967295 + // + // static_cast(-1) = 4294967295 + // clamp_cast(-1) = 0 + // + // static_cast(32768u) = -32768 + // clamp_cast(32768u) = 32767 + + template + constexpr typename std::enable_if::value, Dst>::type + constexpr_clamp_cast(Src src) { + static_assert( + std::is_integral::value && sizeof(Dst) <= sizeof(int64_t), + "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); + + using L = std::numeric_limits; + // clang-format off + return + // Check if Src and Dst have same signedness. + std::is_signed::value == std::is_signed::value + ? ( + // Src and Dst have same signedness. If sizeof(Src) <= sizeof(Dst), + // we can safely convert Src to Dst without any loss of accuracy. + sizeof(Src) <= sizeof(Dst) ? Dst(src) : + // If Src is larger in size, we need to clamp it to valid range in Dst. + Dst(constexpr_clamp(src, Src(L::min()), Src(L::max())))) + // Src and Dst have different signedness. + // Check if it's signed -> unsigend cast. + : std::is_signed::value && std::is_unsigned::value + ? ( + // If src < 0, the result should be 0. + src < 0 ? Dst(0) : + // Otherwise, src >= 0. If src can fit into Dst, we can safely cast it + // without loss of accuracy. + sizeof(Src) <= sizeof(Dst) ? Dst(src) : + // If Src is larger in size than Dst, we need to ensure the result is + // at most Dst MAX. + Dst(constexpr_min(src, Src(L::max())))) + // It's unsigned -> signed cast. + : ( + // Since Src is unsigned, and Dst is signed, Src can fit into Dst only + // when sizeof(Src) < sizeof(Dst). + sizeof(Src) < sizeof(Dst) ? Dst(src) : + // If Src does not fit into Dst, we need to ensure the result is at most + // Dst MAX. + Dst(constexpr_min(src, Src(L::max())))); + // clang-format on + } + + namespace detail { + // Upper/lower bound values that could be accurately represented in both + // integral and float point types. + constexpr double kClampCastLowerBoundDoubleToInt64F = -9223372036854774784.0; + constexpr double kClampCastUpperBoundDoubleToInt64F = 9223372036854774784.0; + constexpr double kClampCastUpperBoundDoubleToUInt64F = 18446744073709549568.0; + + constexpr float kClampCastLowerBoundFloatToInt32F = -2147483520.0f; + constexpr float kClampCastUpperBoundFloatToInt32F = 2147483520.0f; + constexpr float kClampCastUpperBoundFloatToUInt32F = 4294967040.0f; + + // This works the same as constexpr_clamp, but the comparison are done in Src + // to prevent any implicit promotions. + template + constexpr D constexpr_clamp_cast_helper(S src, S sl, S su, D dl, D du) { + return src < sl ? dl : (src > su ? du : D(src)); + } + } // namespace detail + + template + constexpr typename std::enable_if::value, Dst>::type + constexpr_clamp_cast(Src src) { + static_assert( + std::is_integral::value && sizeof(Dst) <= sizeof(int64_t), + "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); + + using L = std::numeric_limits; + // clang-format off + return + // Special case: cast NaN into 0. + constexpr_isnan(src) ? Dst(0) : + // using `sizeof(Src) > sizeof(Dst)` as a heuristic that Dst can be + // represented in Src without loss of accuracy. + // see: https://en.wikipedia.org/wiki/Floating-point_arithmetic + sizeof(Src) > sizeof(Dst) ? + detail::constexpr_clamp_cast_helper( + src, Src(L::min()), Src(L::max()), L::min(), L::max()) : + // sizeof(Src) < sizeof(Dst) only happens when doing cast of + // 32bit float -> u/int64_t. + // Losslessly promote float into double, change into double -> u/int64_t. + sizeof(Src) < sizeof(Dst) ? ( + src >= 0.0 + ? constexpr_clamp_cast( + constexpr_clamp_cast(double(src))) + : constexpr_clamp_cast( + constexpr_clamp_cast(double(src)))) : + // The following are for sizeof(Src) == sizeof(Dst). + std::is_same::value && std::is_same::value ? + detail::constexpr_clamp_cast_helper( + double(src), + detail::kClampCastLowerBoundDoubleToInt64F, + detail::kClampCastUpperBoundDoubleToInt64F, + L::min(), + L::max()) : + std::is_same::value && std::is_same::value ? + detail::constexpr_clamp_cast_helper( + double(src), + 0.0, + detail::kClampCastUpperBoundDoubleToUInt64F, + L::min(), + L::max()) : + std::is_same::value && std::is_same::value ? + detail::constexpr_clamp_cast_helper( + float(src), + detail::kClampCastLowerBoundFloatToInt32F, + detail::kClampCastUpperBoundFloatToInt32F, + L::min(), + L::max()) : + detail::constexpr_clamp_cast_helper( + float(src), + 0.0f, + detail::kClampCastUpperBoundFloatToUInt32F, + L::min(), + L::max()); + // clang-format on + } + + } // namespace folly diff --git a/vnext/Folly/TEMP_UntilFollyUpdate/dynamic-inl.h b/vnext/Folly/TEMP_UntilFollyUpdate/dynamic-inl.h index 4495e2ac88b..a90471ff4dd 100644 --- a/vnext/Folly/TEMP_UntilFollyUpdate/dynamic-inl.h +++ b/vnext/Folly/TEMP_UntilFollyUpdate/dynamic-inl.h @@ -14,1098 +14,1097 @@ * limitations under the License. */ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace folly { -namespace detail { - -struct DynamicHasher { - using is_transparent = void; - using folly_is_avalanching = std::true_type; - - size_t operator()(dynamic const& d) const { return d.hash(); } - - template - std::enable_if_t::value, size_t> - operator()(T const& val) const { - // keep consistent with dynamic::hash() for strings - return Hash()(static_cast(val)); - } -}; - -struct DynamicKeyEqual { - using is_transparent = void; - - bool operator()(const dynamic& lhs, const dynamic& rhs) const { - return std::equal_to()(lhs, rhs); - } - - // Dynamic objects contains a map. At least one of the - // operands should be a dynamic. Hence, an operator() where both operands are - // convertible to StringPiece is unnecessary. - template - std::enable_if_t< - std::is_convertible::value && - std::is_convertible::value, - bool> - operator()(A const& lhs, B const& rhs) const = delete; - - template - std::enable_if_t::value, bool> operator()( - A const& lhs, dynamic const& rhs) const { - return FOLLY_LIKELY(rhs.type() == dynamic::Type::STRING) && - std::equal_to()(lhs, rhs.stringPiece()); - } - - template - std::enable_if_t::value, bool> operator()( - dynamic const& lhs, B const& rhs) const { - return FOLLY_LIKELY(lhs.type() == dynamic::Type::STRING) && - std::equal_to()(lhs.stringPiece(), rhs); - } -}; -} // namespace detail -} // namespace folly - -////////////////////////////////////////////////////////////////////// - -/* clang-format off */ -// This is a higher-order preprocessor macro to aid going from runtime -// types to the compile time type system. - -#define FB_DYNAMIC_APPLY(type, apply) \ - do { \ - FOLLY_PUSH_WARNING FOLLY_CLANG_DISABLE_WARNING("-Wcovered-switch-default") \ - switch ((type)) { \ - case NULLT: \ - apply(std::nullptr_t); \ - break; \ - case ARRAY: \ - apply(Array); \ - break; \ - case BOOL: \ - apply(bool); \ - break; \ - case DOUBLE: \ - apply(double); \ - break; \ - case INT64: \ - apply(int64_t); \ - break; \ - case OBJECT: \ - apply(ObjectImpl); \ - break; \ - case STRING: \ - apply(std::string); \ - break; \ - default: \ - abort(); \ - } \ - FOLLY_POP_WARNING \ - } while (0) -/* clang-format on */ -////////////////////////////////////////////////////////////////////// - -namespace folly { - -struct FOLLY_EXPORT TypeError : std::runtime_error { - explicit TypeError(const std::string& expected, dynamic::Type actual); - explicit TypeError( - const std::string& expected, - dynamic::Type actual1, - dynamic::Type actual2); -}; - -////////////////////////////////////////////////////////////////////// - -namespace detail { - -// This helper is used in destroy() to be able to run destructors on -// types like "int64_t" without a compiler error. -struct Destroy { - template - static void destroy(T* t) { - t->~T(); - } -}; - -/* - * Helper for implementing numeric conversions in operators on - * numbers. Just promotes to double when one of the arguments is - * double, or throws if either is not a numeric type. - */ -template