Rewrite CompactValue to avoid undefined behavior from the use of a union for type-punning #1154
@@ -9,6 +9,11 @@
|
|||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_bit_cast
|
||||||
|
#include <bit>
|
||||||
|
#else
|
||||||
|
#include <cstring>
|
||||||
|
#endif
|
||||||
|
|||||||
#include "YGValue.h"
|
#include "YGValue.h"
|
||||||
#include "YGMacros.h"
|
#include "YGMacros.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -55,30 +60,28 @@ public:
|
|||||||
if (value == 0.0f || (value < LOWER_BOUND && value > -LOWER_BOUND)) {
|
if (value == 0.0f || (value < LOWER_BOUND && value > -LOWER_BOUND)) {
|
||||||
constexpr auto zero =
|
constexpr auto zero =
|
||||||
Unit == YGUnitPercent ? ZERO_BITS_PERCENT : ZERO_BITS_POINT;
|
Unit == YGUnitPercent ? ZERO_BITS_PERCENT : ZERO_BITS_POINT;
|
||||||
return {Payload{zero}};
|
return {zero};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto upperBound =
|
constexpr auto upperBound = Unit == YGUnitPercent ? UPPER_BOUND_PERCENT : UPPER_BOUND_POINT;
|
||||||
Unit == YGUnitPercent ? UPPER_BOUND_PERCENT : UPPER_BOUND_POINT;
|
|
||||||
if (value > upperBound || value < -upperBound) {
|
if (value > upperBound || value < -upperBound) {
|
||||||
value = copysignf(upperBound, value);
|
value = copysignf(upperBound, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t unitBit = Unit == YGUnitPercent ? PERCENT_BIT : 0;
|
uint32_t unitBit = Unit == YGUnitPercent ? PERCENT_BIT : 0;
|
||||||
auto data = Payload{value};
|
auto data = asU32(value);
|
||||||
data.repr -= BIAS;
|
data -= BIAS;
|
||||||
data.repr |= unitBit;
|
data |= unitBit;
|
||||||
return {data};
|
return {data};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <YGUnit Unit>
|
template <YGUnit Unit>
|
||||||
static CompactValue ofMaybe(float value) noexcept {
|
static CompactValue ofMaybe(float value) noexcept {
|
||||||
return std::isnan(value) || std::isinf(value) ? ofUndefined()
|
return std::isnan(value) || std::isinf(value) ? ofUndefined() : of<Unit>(value);
|
||||||
: of<Unit>(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr CompactValue ofZero() noexcept {
|
static constexpr CompactValue ofZero() noexcept {
|
||||||
return CompactValue{Payload{ZERO_BITS_POINT}};
|
return CompactValue{ZERO_BITS_POINT};
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr CompactValue ofUndefined() noexcept {
|
static constexpr CompactValue ofUndefined() noexcept {
|
||||||
@@ -86,13 +89,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static constexpr CompactValue ofAuto() noexcept {
|
static constexpr CompactValue ofAuto() noexcept {
|
||||||
return CompactValue{Payload{AUTO_BITS}};
|
return CompactValue{AUTO_BITS};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr CompactValue() noexcept
|
constexpr CompactValue() noexcept : repr_(0x7FC00000) {}
|
||||||
: payload_(std::numeric_limits<float>::quiet_NaN()) {}
|
|
||||||
|
|
||||||
CompactValue(const YGValue& x) noexcept : payload_(uint32_t{0}) {
|
CompactValue(const YGValue &x) noexcept : repr_(uint32_t{0}) {
|
||||||
switch (x.unit) {
|
switch (x.unit) {
|
||||||
case YGUnitUndefined:
|
case YGUnitUndefined:
|
||||||
*this = ofUndefined();
|
*this = ofUndefined();
|
||||||
@@ -110,7 +112,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator YGValue() const noexcept {
|
operator YGValue() const noexcept {
|
||||||
switch (payload_.repr) {
|
switch (repr_) {
|
||||||
case AUTO_BITS:
|
case AUTO_BITS:
|
||||||
return YGValueAuto;
|
return YGValueAuto;
|
||||||
case ZERO_BITS_POINT:
|
case ZERO_BITS_POINT:
|
||||||
@@ -119,34 +121,27 @@ public:
|
|||||||
return YGValue{0.0f, YGUnitPercent};
|
return YGValue{0.0f, YGUnitPercent};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::isnan(payload_.value)) {
|
if (std::isnan(asFloat(repr_))) {
|
||||||
return YGValueUndefined;
|
return YGValueUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = payload_;
|
auto data = repr_;
|
||||||
data.repr &= ~PERCENT_BIT;
|
data &= ~PERCENT_BIT;
|
||||||
data.repr += BIAS;
|
data += BIAS;
|
||||||
|
|
||||||
return YGValue{
|
return YGValue{asFloat(data), repr_ & 0x40000000 ? YGUnitPercent : YGUnitPoint};
|
||||||
data.value, payload_.repr & 0x40000000 ? YGUnitPercent : YGUnitPoint};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUndefined() const noexcept {
|
bool isUndefined() const noexcept {
|
||||||
return (
|
return (repr_ != AUTO_BITS && repr_ != ZERO_BITS_POINT && repr_ != ZERO_BITS_PERCENT && std::isnan(asFloat(repr_)));
|
||||||
payload_.repr != AUTO_BITS && payload_.repr != ZERO_BITS_POINT &&
|
|
||||||
payload_.repr != ZERO_BITS_PERCENT && std::isnan(payload_.value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAuto() const noexcept { return payload_.repr == AUTO_BITS; }
|
bool isAuto() const noexcept {
|
||||||
|
return repr_ == AUTO_BITS;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
union Payload {
|
uint32_t repr_;
|
||||||
float value;
|
|
||||||
uint32_t repr;
|
|
||||||
Payload() = delete;
|
|
||||||
constexpr Payload(uint32_t r) : repr(r) {}
|
|
||||||
constexpr Payload(float v) : value(v) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr uint32_t BIAS = 0x20000000;
|
static constexpr uint32_t BIAS = 0x20000000;
|
||||||
static constexpr uint32_t PERCENT_BIT = 0x40000000;
|
static constexpr uint32_t PERCENT_BIT = 0x40000000;
|
||||||
@@ -157,13 +152,36 @@ private:
|
|||||||
static constexpr uint32_t ZERO_BITS_POINT = 0x7f8f0f0f;
|
static constexpr uint32_t ZERO_BITS_POINT = 0x7f8f0f0f;
|
||||||
static constexpr uint32_t ZERO_BITS_PERCENT = 0x7f80f0f0;
|
static constexpr uint32_t ZERO_BITS_PERCENT = 0x7f80f0f0;
|
||||||
|
|
||||||
constexpr CompactValue(Payload data) noexcept : payload_(data) {}
|
constexpr CompactValue(uint32_t data) noexcept : repr_(data) {}
|
||||||
|
|
||||||
Payload payload_;
|
VISIBLE_FOR_TESTING uint32_t repr() {
|
||||||
|
return repr_;
|
||||||
|
}
|
||||||
|
|
||||||
VISIBLE_FOR_TESTING uint32_t repr() { return payload_.repr; }
|
static uint32_t asU32(float f) {
|
||||||
|
#ifdef __cpp_lib_bit_cast
|
||||||
|
return std::bit_cast<uint32_t>(f);
|
||||||
|
#else
|
||||||
|
uint32_t u;
|
||||||
|
static_assert(sizeof(u) == sizeof(f), "uint32_t and float must have the same size");
|
||||||
|
std::memcpy(&u, &f, sizeof(f));
|
||||||
|
return u;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static float asFloat(uint32_t u) {
|
||||||
|
#ifdef __cpp_lib_bit_cast
|
||||||
|
return std::bit_cast<float>(u);
|
||||||
|
#else
|
||||||
|
float f;
|
||||||
|
static_assert(sizeof(f) == sizeof(u), "uint32_t and float must have the same size");
|
||||||
|
std::memcpy(&f, &u, sizeof(u));
|
||||||
|
return f;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
CompactValue CompactValue::of<YGUnitUndefined>(float) noexcept = delete;
|
CompactValue CompactValue::of<YGUnitUndefined>(float) noexcept = delete;
|
||||||
template <>
|
template <>
|
||||||
@@ -174,7 +192,7 @@ template <>
|
|||||||
CompactValue CompactValue::ofMaybe<YGUnitAuto>(float) noexcept = delete;
|
CompactValue CompactValue::ofMaybe<YGUnitAuto>(float) noexcept = delete;
|
||||||
|
|
||||||
constexpr bool operator==(CompactValue a, CompactValue b) noexcept {
|
constexpr bool operator==(CompactValue a, CompactValue b) noexcept {
|
||||||
return a.payload_.repr == b.payload_.repr;
|
return a.repr_ == b.repr_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool operator!=(CompactValue a, CompactValue b) noexcept {
|
constexpr bool operator!=(CompactValue a, CompactValue b) noexcept {
|
||||||
|
Reference in New Issue
Block a user
Looks like internal builds are failing because std::memcpy requires
#include <cstring>