CompactValue.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the LICENSE
  5. * file in the root directory of this source tree.
  6. */
  7. #pragma once
  8. #include "YGValue.h"
  9. #include <cmath>
  10. #include <cstdint>
  11. #include <limits>
  12. static_assert(
  13. std::numeric_limits<float>::is_iec559,
  14. "facebook::yoga::detail::CompactValue only works with IEEE754 floats");
  15. #ifdef YOGA_COMPACT_VALUE_TEST
  16. #define VISIBLE_FOR_TESTING public:
  17. #else
  18. #define VISIBLE_FOR_TESTING private:
  19. #endif
  20. namespace facebook {
  21. namespace yoga {
  22. namespace detail {
  23. // This class stores YGValue in 32 bits.
  24. // - The value does not matter for Undefined and Auto. NaNs are used for their
  25. // representation.
  26. // - To differentiate between Point and Percent, one exponent bit is used.
  27. // Supported the range [0x40, 0xbf] (0xbf is inclusive for point, but
  28. // exclusive for percent).
  29. // - Value ranges:
  30. // points: 1.08420217e-19f to 36893485948395847680
  31. // 0x00000000 0x3fffffff
  32. // percent: 1.08420217e-19f to 18446742974197923840
  33. // 0x40000000 0x7f7fffff
  34. // - Zero is supported, negative zero is not
  35. // - values outside of the representable range are clamped
  36. class CompactValue {
  37. friend constexpr bool operator==(CompactValue, CompactValue) noexcept;
  38. public:
  39. static constexpr auto LOWER_BOUND = 1.08420217e-19f;
  40. static constexpr auto UPPER_BOUND_POINT = 36893485948395847680.0f;
  41. static constexpr auto UPPER_BOUND_PERCENT = 18446742974197923840.0f;
  42. template <YGUnit Unit>
  43. static CompactValue of(float value) noexcept {
  44. if (value == 0.0f || (value < LOWER_BOUND && value > -LOWER_BOUND)) {
  45. constexpr auto zero =
  46. Unit == YGUnitPercent ? ZERO_BITS_PERCENT : ZERO_BITS_POINT;
  47. return {Payload{zero}};
  48. }
  49. constexpr auto upperBound =
  50. Unit == YGUnitPercent ? UPPER_BOUND_PERCENT : UPPER_BOUND_POINT;
  51. if (value > upperBound || value < -upperBound) {
  52. value = copysignf(upperBound, value);
  53. }
  54. uint32_t unitBit = Unit == YGUnitPercent ? PERCENT_BIT : 0;
  55. auto data = Payload{value};
  56. data.repr -= BIAS;
  57. data.repr |= unitBit;
  58. return {data};
  59. }
  60. template <YGUnit Unit>
  61. static CompactValue ofMaybe(float value) noexcept {
  62. return std::isnan(value) || std::isinf(value) ? ofUndefined()
  63. : of<Unit>(value);
  64. }
  65. static constexpr CompactValue ofZero() noexcept {
  66. return CompactValue{Payload{ZERO_BITS_POINT}};
  67. }
  68. static constexpr CompactValue ofUndefined() noexcept {
  69. return CompactValue{};
  70. }
  71. static constexpr CompactValue ofAuto() noexcept {
  72. return CompactValue{Payload{AUTO_BITS}};
  73. }
  74. constexpr CompactValue() noexcept
  75. : payload_(std::numeric_limits<float>::quiet_NaN()) {}
  76. CompactValue(const YGValue& x) noexcept : payload_(uint32_t{0}) {
  77. switch (x.unit) {
  78. case YGUnitUndefined:
  79. *this = ofUndefined();
  80. break;
  81. case YGUnitAuto:
  82. *this = ofAuto();
  83. break;
  84. case YGUnitPoint:
  85. *this = of<YGUnitPoint>(x.value);
  86. break;
  87. case YGUnitPercent:
  88. *this = of<YGUnitPercent>(x.value);
  89. break;
  90. }
  91. }
  92. operator YGValue() const noexcept {
  93. switch (payload_.repr) {
  94. case AUTO_BITS:
  95. return YGValueAuto;
  96. case ZERO_BITS_POINT:
  97. return YGValue{0.0f, YGUnitPoint};
  98. case ZERO_BITS_PERCENT:
  99. return YGValue{0.0f, YGUnitPercent};
  100. }
  101. if (std::isnan(payload_.value)) {
  102. return YGValueUndefined;
  103. }
  104. auto data = payload_;
  105. data.repr &= ~PERCENT_BIT;
  106. data.repr += BIAS;
  107. return YGValue{data.value,
  108. payload_.repr & 0x40000000 ? YGUnitPercent : YGUnitPoint};
  109. }
  110. bool isUndefined() const noexcept {
  111. return (
  112. payload_.repr != AUTO_BITS && payload_.repr != ZERO_BITS_POINT &&
  113. payload_.repr != ZERO_BITS_PERCENT && std::isnan(payload_.value));
  114. }
  115. bool isAuto() const noexcept {
  116. return payload_.repr == AUTO_BITS;
  117. }
  118. private:
  119. union Payload {
  120. float value;
  121. uint32_t repr;
  122. Payload() = delete;
  123. constexpr Payload(uint32_t r) : repr(r) {}
  124. constexpr Payload(float v) : value(v) {}
  125. };
  126. static constexpr uint32_t BIAS = 0x20000000;
  127. static constexpr uint32_t PERCENT_BIT = 0x40000000;
  128. // these are signaling NaNs with specific bit pattern as payload they will be
  129. // silenced whenever going through an FPU operation on ARM + x86
  130. static constexpr uint32_t AUTO_BITS = 0x7faaaaaa;
  131. static constexpr uint32_t ZERO_BITS_POINT = 0x7f8f0f0f;
  132. static constexpr uint32_t ZERO_BITS_PERCENT = 0x7f80f0f0;
  133. constexpr CompactValue(Payload data) noexcept : payload_(data) {}
  134. Payload payload_;
  135. VISIBLE_FOR_TESTING uint32_t repr() {
  136. return payload_.repr;
  137. }
  138. };
  139. template <>
  140. CompactValue CompactValue::of<YGUnitUndefined>(float) noexcept = delete;
  141. template <>
  142. CompactValue CompactValue::of<YGUnitAuto>(float) noexcept = delete;
  143. template <>
  144. CompactValue CompactValue::ofMaybe<YGUnitUndefined>(float) noexcept = delete;
  145. template <>
  146. CompactValue CompactValue::ofMaybe<YGUnitAuto>(float) noexcept = delete;
  147. constexpr bool operator==(CompactValue a, CompactValue b) noexcept {
  148. return a.payload_.repr == b.payload_.repr;
  149. }
  150. constexpr bool operator!=(CompactValue a, CompactValue b) noexcept {
  151. return !(a == b);
  152. }
  153. } // namespace detail
  154. } // namespace yoga
  155. } // namespace facebook