Introduce CompactValue
Summary: @public `CompactValue` represents a `YGValue` in 32bits instead of 64. This comes at the cost of a range limitation, as one exponent bit is borrowed for the unit. *Undefined* and *Auto* have no magnitude, and are represented as *NaN* values. The data structure is meant to be used as a field type on `YGStyle` to save memory. This is header-only for efficient inlining. Reviewed By: jackerghan, aCorrado Differential Revision: D13187211 fbshipit-source-id: 16e3ffad592e38e2493e4f7c8b952d372e449846
This commit is contained in:
committed by
Facebook Github Bot
parent
c5f2444048
commit
8bc89651d6
2
BUCK
2
BUCK
@@ -20,6 +20,7 @@ TEST_COMPILER_FLAGS = BASE_COMPILER_FLAGS + GMOCK_OVERRIDE_FLAGS + [
|
||||
yoga_cxx_library(
|
||||
name = "yoga",
|
||||
srcs = glob(["yoga/*.cpp"]),
|
||||
headers = subdir_glob([("", "yoga/**/*.h")]),
|
||||
header_namespace = "",
|
||||
exported_headers = subdir_glob([("", "yoga/*.h")]),
|
||||
compiler_flags = COMPILER_FLAGS,
|
||||
@@ -34,6 +35,7 @@ yoga_cxx_library(
|
||||
yoga_cxx_test(
|
||||
name = "YogaTests",
|
||||
srcs = glob(["tests/*.cpp"]),
|
||||
headers = subdir_glob([("", "yoga/**/*.h")]),
|
||||
compiler_flags = TEST_COMPILER_FLAGS,
|
||||
contacts = ["emilsj@fb.com"],
|
||||
visibility = ["PUBLIC"],
|
||||
|
349
tests/CompactValueTest.cpp
Normal file
349
tests/CompactValueTest.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the LICENSE
|
||||
* file in the root directory of this source tree.
|
||||
*/
|
||||
#define YOGA_COMPACT_VALUE_TEST
|
||||
|
||||
#include <yoga/CompactValue.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <cmath>
|
||||
|
||||
using facebook::yoga::detail::CompactValue;
|
||||
|
||||
const auto tooSmall = nextafterf(CompactValue::LOWER_BOUND, -INFINITY);
|
||||
const auto tooLargePoints =
|
||||
nextafterf(CompactValue::UPPER_BOUND_POINT, INFINITY);
|
||||
const auto tooLargePercent =
|
||||
nextafterf(CompactValue::UPPER_BOUND_PERCENT, INFINITY);
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_undefined) {
|
||||
auto c = CompactValue{YGValue{12.5f, YGUnitUndefined}};
|
||||
YGValue v = c;
|
||||
ASSERT_EQ(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{-1.25, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{25, YGUnitPercent}));
|
||||
ASSERT_TRUE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_auto) {
|
||||
auto c = CompactValue{YGValue{0, YGUnitAuto}};
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_EQ(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{-1.25, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{25, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_TRUE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_zero_points) {
|
||||
auto c = CompactValue{YGValue{0, YGUnitPoint}};
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{0, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{0, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_lower_bound_points) {
|
||||
auto c = CompactValue({YGValue{CompactValue::LOWER_BOUND, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{CompactValue::LOWER_BOUND, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{CompactValue::LOWER_BOUND, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_negative_lower_bound_points) {
|
||||
auto c = CompactValue({YGValue{-CompactValue::LOWER_BOUND, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{-CompactValue::LOWER_BOUND, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{-CompactValue::LOWER_BOUND, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_clamps_smaller_than_lower_bound_points_to_zero) {
|
||||
auto c = CompactValue({YGValue{tooSmall, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{0, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{0, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(
|
||||
YogaTest,
|
||||
compact_value_clamps_greater_than_negative_lower_bound_points_to_zero) {
|
||||
auto c = CompactValue({YGValue{-tooSmall, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{0, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{0, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_upper_bound_points) {
|
||||
auto c =
|
||||
CompactValue({YGValue{CompactValue::UPPER_BOUND_POINT, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{CompactValue::UPPER_BOUND_POINT, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{CompactValue::UPPER_BOUND_POINT, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_negative_upper_bound_points) {
|
||||
auto c =
|
||||
CompactValue({YGValue{-CompactValue::UPPER_BOUND_POINT, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{-CompactValue::UPPER_BOUND_POINT, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{-CompactValue::UPPER_BOUND_POINT, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(
|
||||
YogaTest,
|
||||
compact_value_clamps_greater_than__upper_bound_points_to_upper_bound) {
|
||||
auto c = CompactValue({YGValue{tooLargePoints, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{CompactValue::UPPER_BOUND_POINT, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{CompactValue::UPPER_BOUND_POINT, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(
|
||||
YogaTest,
|
||||
compact_value_clamps_smaller_than_negative_upper_bound_points_to_upper_bound) {
|
||||
auto c = CompactValue({YGValue{-tooLargePoints, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{-CompactValue::UPPER_BOUND_POINT, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{-CompactValue::UPPER_BOUND_POINT, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_one_point) {
|
||||
auto c = CompactValue({YGValue{1, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{1, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{1, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_negative_one_point) {
|
||||
auto c = CompactValue({YGValue{-1, YGUnitPoint}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_EQ(v, (YGValue{-1, YGUnitPoint}));
|
||||
ASSERT_NE(v, (YGValue{-1, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_zero_percent) {
|
||||
auto c = CompactValue{YGValue{0, YGUnitPercent}};
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{0, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{0, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_lower_bound_percent) {
|
||||
auto c = CompactValue({YGValue{CompactValue::LOWER_BOUND, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{CompactValue::LOWER_BOUND, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{CompactValue::LOWER_BOUND, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_negative_lower_bound_percent) {
|
||||
auto c = CompactValue({YGValue{-CompactValue::LOWER_BOUND, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{-CompactValue::LOWER_BOUND, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{-CompactValue::LOWER_BOUND, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_clamps_smaller_than_lower_bound_percent_to_zero) {
|
||||
auto c = CompactValue({YGValue{tooSmall, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{0, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{0, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(
|
||||
YogaTest,
|
||||
compact_value_clamps_greater_than_negative_lower_bound_percent_to_zero) {
|
||||
auto c = CompactValue({YGValue{-tooSmall, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{0, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{0, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_upper_bound_percent) {
|
||||
auto c =
|
||||
CompactValue({YGValue{CompactValue::UPPER_BOUND_PERCENT, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{CompactValue::UPPER_BOUND_PERCENT, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{CompactValue::UPPER_BOUND_PERCENT, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_negative_upper_bound_percent) {
|
||||
auto c = CompactValue(
|
||||
{YGValue{-CompactValue::UPPER_BOUND_PERCENT, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{-CompactValue::UPPER_BOUND_PERCENT, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{-CompactValue::UPPER_BOUND_PERCENT, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(
|
||||
YogaTest,
|
||||
compact_value_clamps_greater_than_upper_bound_percent_to_upper_bound) {
|
||||
auto c = CompactValue({YGValue{tooLargePercent, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{CompactValue::UPPER_BOUND_PERCENT, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{CompactValue::UPPER_BOUND_PERCENT, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(
|
||||
YogaTest,
|
||||
compact_value_clamps_smaller_than_negative_upper_bound_percent_to_upper_bound) {
|
||||
auto c = CompactValue({YGValue{-tooLargePercent, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{-CompactValue::UPPER_BOUND_PERCENT, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{-CompactValue::UPPER_BOUND_PERCENT, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_one_percent) {
|
||||
auto c = CompactValue({YGValue{1, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{1, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{1, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_can_represent_negative_one_percent) {
|
||||
auto c = CompactValue({YGValue{-1, YGUnitPercent}});
|
||||
YGValue v = c;
|
||||
ASSERT_NE(v, YGValueUndefined);
|
||||
ASSERT_NE(v, YGValueAuto);
|
||||
ASSERT_NE(v, (YGValue{-1, YGUnitPoint}));
|
||||
ASSERT_EQ(v, (YGValue{-1, YGUnitPercent}));
|
||||
ASSERT_FALSE(c.isUndefined());
|
||||
ASSERT_FALSE(c.isAuto());
|
||||
}
|
||||
|
||||
TEST(YogaTest, dedicated_unit_factories) {
|
||||
ASSERT_EQ(CompactValue::ofUndefined(), CompactValue(YGValueUndefined));
|
||||
ASSERT_EQ(CompactValue::ofAuto(), CompactValue(YGValueAuto));
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPoint>(-9876.5f),
|
||||
CompactValue(YGValue{-9876.5f, YGUnitPoint}));
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPercent>(123.456f),
|
||||
CompactValue(YGValue{123.456f, YGUnitPercent}));
|
||||
}
|
||||
|
||||
TEST(YogaTest, dedicated_unit_maybe_factories) {
|
||||
ASSERT_EQ(
|
||||
CompactValue::ofMaybe<YGUnitPoint>(-9876.5f),
|
||||
CompactValue(YGValue{-9876.5f, YGUnitPoint}));
|
||||
ASSERT_EQ(
|
||||
CompactValue::ofMaybe<YGUnitPoint>(YGUndefined),
|
||||
CompactValue(YGValueUndefined));
|
||||
ASSERT_EQ(
|
||||
CompactValue::ofMaybe<YGUnitPercent>(123.456f),
|
||||
CompactValue(YGValue{123.456f, YGUnitPercent}));
|
||||
ASSERT_EQ(
|
||||
CompactValue::ofMaybe<YGUnitPercent>(YGUndefined),
|
||||
CompactValue(YGValueUndefined));
|
||||
}
|
||||
|
||||
TEST(YogaTest, can_be_assigned_from_YGValue) {
|
||||
CompactValue c{};
|
||||
|
||||
YGValue v{2.0f, YGUnitPercent};
|
||||
c = v;
|
||||
ASSERT_EQ((YGValue)c, v);
|
||||
|
||||
c = YGValue{123, YGUnitPoint};
|
||||
ASSERT_EQ((YGValue)c, (YGValue{123, YGUnitPoint}));
|
||||
}
|
||||
|
||||
TEST(YogaTest, compact_value_bound_representations) {
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPoint>(CompactValue::LOWER_BOUND).repr(),
|
||||
uint32_t{0});
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPoint>(CompactValue::UPPER_BOUND_POINT).repr(),
|
||||
uint32_t{0x3fffffff});
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPercent>(CompactValue::LOWER_BOUND).repr(),
|
||||
uint32_t{0x40000000});
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPercent>(CompactValue::UPPER_BOUND_PERCENT).repr(),
|
||||
uint32_t{0x7f7fffff});
|
||||
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPoint>(-CompactValue::LOWER_BOUND).repr(),
|
||||
uint32_t{0x80000000});
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPoint>(-CompactValue::UPPER_BOUND_POINT).repr(),
|
||||
uint32_t{0xbfffffff});
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPercent>(-CompactValue::LOWER_BOUND).repr(),
|
||||
uint32_t{0xc0000000});
|
||||
ASSERT_EQ(
|
||||
CompactValue::of<YGUnitPercent>(-CompactValue::UPPER_BOUND_PERCENT)
|
||||
.repr(),
|
||||
uint32_t{0xff7fffff});
|
||||
}
|
182
yoga/CompactValue.h
Normal file
182
yoga/CompactValue.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the LICENSE
|
||||
* file in the root directory of this source tree.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <yoga/YGValue.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
static_assert(
|
||||
std::numeric_limits<float>::is_iec559,
|
||||
"facebook::yoga::detail::CompactValue only works with IEEE754 floats");
|
||||
|
||||
#ifdef YOGA_COMPACT_VALUE_TEST
|
||||
#define VISIBLE_FOR_TESTING public:
|
||||
#else
|
||||
#define VISIBLE_FOR_TESTING private:
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace yoga {
|
||||
namespace detail {
|
||||
|
||||
// This class stores YGValue in 32 bits.
|
||||
// - The value does not matter for Undefined and Auto. NaNs are used for their
|
||||
// representation.
|
||||
// - To differentiate between Point and Percent, one exponent bit is used.
|
||||
// Supported the range [0x40, 0xbf] (0xbf is inclusive for point, but
|
||||
// exclusive for percent).
|
||||
// - Value ranges:
|
||||
// points: 1.08420217e-19f to 36893485948395847680
|
||||
// 0x00000000 0x3fffffff
|
||||
// percent: 1.08420217e-19f to 18446742974197923840
|
||||
// 0x40000000 0x7f7fffff
|
||||
// - Zero is supported, negative zero is not
|
||||
// - values outside of the representable range are clamped
|
||||
class CompactValue {
|
||||
friend constexpr bool operator==(CompactValue, CompactValue) noexcept;
|
||||
|
||||
public:
|
||||
static constexpr auto LOWER_BOUND = 1.08420217e-19f;
|
||||
static constexpr auto UPPER_BOUND_POINT = 36893485948395847680.0f;
|
||||
static constexpr auto UPPER_BOUND_PERCENT = 18446742974197923840.0f;
|
||||
|
||||
template <YGUnit Unit>
|
||||
static CompactValue of(float value) noexcept {
|
||||
if (value == 0.0f || (value < LOWER_BOUND && value > -LOWER_BOUND)) {
|
||||
constexpr auto zero =
|
||||
Unit == YGUnitPercent ? ZERO_BITS_PERCENT : ZERO_BITS_POINT;
|
||||
return {Payload{zero}};
|
||||
}
|
||||
|
||||
constexpr auto upperBound =
|
||||
Unit == YGUnitPercent ? UPPER_BOUND_PERCENT : UPPER_BOUND_POINT;
|
||||
if (value > upperBound || value < -upperBound) {
|
||||
value = copysignf(upperBound, value);
|
||||
}
|
||||
|
||||
uint32_t unitBit = Unit == YGUnitPercent ? PERCENT_BIT : 0;
|
||||
auto data = Payload{value};
|
||||
data.repr -= BIAS;
|
||||
data.repr |= unitBit;
|
||||
return {data};
|
||||
}
|
||||
|
||||
template <YGUnit Unit>
|
||||
static CompactValue ofMaybe(float value) noexcept {
|
||||
return std::isnan(value) ? ofUndefined() : of<Unit>(value);
|
||||
}
|
||||
|
||||
static constexpr CompactValue ofUndefined() noexcept {
|
||||
return CompactValue{};
|
||||
}
|
||||
|
||||
static constexpr CompactValue ofAuto() noexcept {
|
||||
return CompactValue{Payload{AUTO_BITS}};
|
||||
}
|
||||
|
||||
constexpr CompactValue() noexcept
|
||||
: payload_(std::numeric_limits<float>::quiet_NaN()) {}
|
||||
|
||||
CompactValue(const YGValue& x) noexcept : payload_(uint32_t{0}) {
|
||||
switch (x.unit) {
|
||||
case YGUnitUndefined:
|
||||
*this = ofUndefined();
|
||||
break;
|
||||
case YGUnitAuto:
|
||||
*this = ofAuto();
|
||||
break;
|
||||
case YGUnitPoint:
|
||||
*this = of<YGUnitPoint>(x.value);
|
||||
break;
|
||||
case YGUnitPercent:
|
||||
*this = of<YGUnitPercent>(x.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
operator YGValue() const noexcept {
|
||||
switch (payload_.repr) {
|
||||
case AUTO_BITS:
|
||||
return YGValueAuto;
|
||||
case ZERO_BITS_POINT:
|
||||
return YGValue{0.0f, YGUnitPoint};
|
||||
case ZERO_BITS_PERCENT:
|
||||
return YGValue{0.0f, YGUnitPercent};
|
||||
}
|
||||
|
||||
if (std::isnan(payload_.value)) {
|
||||
return YGValueUndefined;
|
||||
}
|
||||
|
||||
auto data = payload_;
|
||||
data.repr &= ~PERCENT_BIT;
|
||||
data.repr += BIAS;
|
||||
|
||||
return YGValue{data.value,
|
||||
payload_.repr & 0x40000000 ? YGUnitPercent : YGUnitPoint};
|
||||
}
|
||||
|
||||
bool isUndefined() const noexcept {
|
||||
return (
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
union Payload {
|
||||
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 PERCENT_BIT = 0x40000000;
|
||||
|
||||
// these are signaling NaNs with specific bit pattern as payload
|
||||
// they will be silenced whenever going through an FPU operation on ARM + x86
|
||||
static constexpr uint32_t AUTO_BITS = 0x7faaaaaa;
|
||||
static constexpr uint32_t ZERO_BITS_POINT = 0x7f8f0f0f;
|
||||
static constexpr uint32_t ZERO_BITS_PERCENT = 0x7f80f0f0;
|
||||
|
||||
constexpr CompactValue(Payload data) noexcept : payload_(data) {}
|
||||
|
||||
Payload payload_;
|
||||
|
||||
VISIBLE_FOR_TESTING uint32_t repr() {
|
||||
return payload_.repr;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
CompactValue CompactValue::of<YGUnitUndefined>(float) noexcept = delete;
|
||||
template <>
|
||||
CompactValue CompactValue::of<YGUnitAuto>(float) noexcept = delete;
|
||||
template <>
|
||||
CompactValue CompactValue::ofMaybe<YGUnitUndefined>(float) noexcept = delete;
|
||||
template <>
|
||||
CompactValue CompactValue::ofMaybe<YGUnitAuto>(float) noexcept = delete;
|
||||
|
||||
constexpr bool operator==(CompactValue a, CompactValue b) noexcept {
|
||||
return a.payload_.repr == b.payload_.repr;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(CompactValue a, CompactValue b) noexcept {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace yoga
|
||||
} // namespace facebook
|
Reference in New Issue
Block a user