From ed3b54b603cc3bf3483812823e06fa166eb1cf71 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Thu, 6 Dec 2018 07:35:08 -0800 Subject: [PATCH] Store `YGFloatOptional` in 32 bits Summary: @public After removing `-ffast-math`, `NaN` can again be used to represent `undefined`. That allows us to remove the additional flag from `YGFloatOptional`, and reduce memory usage. Reviewed By: SidharthGuglani Differential Revision: D13209157 fbshipit-source-id: 21b83c837a78f924a4ec23a9236ca2440b3c8606 --- tests/YGFloatOptionalTest.cpp | 163 ++++++++++++++++++++++++++++++++++ yoga/YGFloatOptional.cpp | 53 +++-------- yoga/YGFloatOptional.h | 32 +++---- yoga/YGNode.cpp | 2 +- 4 files changed, 192 insertions(+), 58 deletions(-) create mode 100644 tests/YGFloatOptionalTest.cpp diff --git a/tests/YGFloatOptionalTest.cpp b/tests/YGFloatOptionalTest.cpp new file mode 100644 index 00000000..2096e5fd --- /dev/null +++ b/tests/YGFloatOptionalTest.cpp @@ -0,0 +1,163 @@ +/** + * 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. + */ +#include +#include +#include + +constexpr auto empty = YGFloatOptional{}; +constexpr auto zero = YGFloatOptional{0.0f}; +constexpr auto one = YGFloatOptional{1.0f}; +constexpr auto positive = YGFloatOptional{1234.5f}; +constexpr auto negative = YGFloatOptional{-9876.5f}; + +TEST(YGFloatOptional, value) { + ASSERT_EQ(zero.getValue(), 0.0f); + ASSERT_EQ(positive.getValue(), 1234.5f); + ASSERT_EQ(negative.getValue(), -9876.5f); + + ASSERT_TRUE(empty.isUndefined()); + ASSERT_FALSE(zero.isUndefined()); + ASSERT_FALSE(negative.isUndefined()); +} + +TEST(YGFloatOptional, equality) { + ASSERT_TRUE(empty == empty); + ASSERT_TRUE(empty == YGUndefined); + ASSERT_FALSE(empty == zero); + ASSERT_FALSE(empty == negative); + ASSERT_FALSE(empty == 12.3f); + + ASSERT_TRUE(zero == zero); + ASSERT_TRUE(zero == 0.0f); + ASSERT_FALSE(zero == positive); + ASSERT_FALSE(zero == -5555.5f); + + ASSERT_TRUE(one == one); + ASSERT_TRUE(one == 1.0f); + ASSERT_FALSE(one == positive); + + ASSERT_TRUE(negative == negative); + ASSERT_TRUE(negative == negative.getValue()); + ASSERT_FALSE(negative == zero); +} + +TEST(YGFloatOptional, inequality) { + ASSERT_FALSE(empty != empty); + ASSERT_FALSE(empty != YGUndefined); + ASSERT_TRUE(empty != zero); + ASSERT_TRUE(empty != negative); + ASSERT_TRUE(empty != 12.3f); + + ASSERT_FALSE(zero != zero); + ASSERT_FALSE(zero != 0.0f); + ASSERT_TRUE(zero != positive); + ASSERT_TRUE(zero != -5555.5f); + + ASSERT_FALSE(one != one); + ASSERT_FALSE(one != 1.0f); + ASSERT_TRUE(one != positive); + + ASSERT_FALSE(negative != negative); + ASSERT_FALSE(negative != negative.getValue()); + ASSERT_TRUE(negative != zero); +} + +TEST(YGFloatOptional, greater) { + ASSERT_FALSE(empty > empty); + ASSERT_FALSE(empty > zero); + ASSERT_FALSE(empty > one); + ASSERT_FALSE(empty > positive); + ASSERT_FALSE(empty > negative); + + ASSERT_TRUE(zero > negative); + ASSERT_FALSE(zero > zero); + ASSERT_FALSE(zero > positive); + ASSERT_FALSE(zero > one); + + ASSERT_TRUE(one > negative); + ASSERT_TRUE(one > zero); + ASSERT_FALSE(one > empty); + ASSERT_FALSE(one > positive); + + ASSERT_TRUE(negative > YGFloatOptional{-INFINITY}); + ASSERT_FALSE(negative > empty); +} + +TEST(YGFloatOptional, lower) { + ASSERT_FALSE(empty < empty); + ASSERT_FALSE(empty < zero); + ASSERT_FALSE(empty < one); + ASSERT_FALSE(empty < positive); + ASSERT_FALSE(empty < negative); + + ASSERT_TRUE(negative < zero); + ASSERT_FALSE(zero < zero); + ASSERT_FALSE(positive < zero); + ASSERT_FALSE(one < zero); + + ASSERT_TRUE(negative < one); + ASSERT_TRUE(zero < one); + ASSERT_FALSE(empty < one); + ASSERT_FALSE(positive < one); + + ASSERT_TRUE(YGFloatOptional{-INFINITY} < negative); + ASSERT_FALSE(empty < negative); +} + +TEST(YGFloatOptional, greater_equals) { + ASSERT_TRUE(empty >= empty); + ASSERT_FALSE(empty >= zero); + ASSERT_FALSE(empty >= one); + ASSERT_FALSE(empty >= positive); + ASSERT_FALSE(empty >= negative); + + ASSERT_TRUE(zero >= negative); + ASSERT_TRUE(zero >= zero); + ASSERT_FALSE(zero >= positive); + ASSERT_FALSE(zero >= one); + + ASSERT_TRUE(one >= negative); + ASSERT_TRUE(one >= zero); + ASSERT_FALSE(one >= empty); + ASSERT_FALSE(one >= positive); + + ASSERT_TRUE(negative >= YGFloatOptional{-INFINITY}); + ASSERT_TRUE(negative >= negative); + ASSERT_FALSE(negative >= empty); +} + +TEST(YGFloatOptional, lower_equals) { + ASSERT_TRUE(empty <= empty); + ASSERT_FALSE(zero <= empty); + ASSERT_FALSE(one <= empty); + ASSERT_FALSE(positive <= empty); + ASSERT_FALSE(negative <= empty); + + ASSERT_TRUE(negative <= zero); + ASSERT_TRUE(zero <= zero); + ASSERT_FALSE(positive <= zero); + ASSERT_FALSE(one <= zero); + + ASSERT_TRUE(negative <= one); + ASSERT_TRUE(zero <= one); + ASSERT_FALSE(empty <= one); + ASSERT_FALSE(positive <= one); + + ASSERT_TRUE(YGFloatOptional{-INFINITY} <= negative); + ASSERT_TRUE(negative <= negative); + ASSERT_FALSE(empty <= negative); +} + +TEST(YGFloatOptional, addition) { + ASSERT_EQ(zero + one, one); + ASSERT_EQ( + negative + positive, + YGFloatOptional{negative.getValue() + positive.getValue()}); + ASSERT_EQ(empty + zero, empty); + ASSERT_EQ(empty + empty, empty); + ASSERT_EQ(negative + empty, empty); +} diff --git a/yoga/YGFloatOptional.cpp b/yoga/YGFloatOptional.cpp index d023dccb..0bf89f29 100644 --- a/yoga/YGFloatOptional.cpp +++ b/yoga/YGFloatOptional.cpp @@ -7,23 +7,13 @@ #include "YGFloatOptional.h" #include #include -#include "Yoga.h" #include "Yoga-internal.h" +#include "Yoga.h" using namespace facebook; -YGFloatOptional::YGFloatOptional(float value) { - if (yoga::isUndefined(value)) { - isUndefined_ = true; - value_ = 0; - } else { - value_ = value; - isUndefined_ = false; - } -} - float YGFloatOptional::getValue() const { - if (isUndefined_) { + if (isUndefined()) { // Abort, accessing a value of an undefined float optional std::cerr << "Tried to get value of an undefined YGFloatOptional\n"; std::exit(EXIT_FAILURE); @@ -31,53 +21,38 @@ float YGFloatOptional::getValue() const { return value_; } -bool YGFloatOptional::operator==(const YGFloatOptional& op) const { - if (isUndefined_ == op.isUndefined()) { - return isUndefined_ || value_ == op.getValue(); - } - return false; +bool YGFloatOptional::operator==(YGFloatOptional op) const { + return value_ == op.value_ || (isUndefined() && op.isUndefined()); } -bool YGFloatOptional::operator!=(const YGFloatOptional& op) const { +bool YGFloatOptional::operator!=(YGFloatOptional op) const { return !(*this == op); } bool YGFloatOptional::operator==(float val) const { - if (yoga::isUndefined(val) == isUndefined_) { - return isUndefined_ || val == value_; - } - return false; + return value_ == val || (isUndefined() && yoga::isUndefined(val)); } bool YGFloatOptional::operator!=(float val) const { return !(*this == val); } -YGFloatOptional YGFloatOptional::operator+(const YGFloatOptional& op) { - if (!isUndefined_ && !op.isUndefined_) { - return YGFloatOptional(value_ + op.value_); - } - return YGFloatOptional(); +YGFloatOptional YGFloatOptional::operator+(YGFloatOptional op) const { + return YGFloatOptional{value_ + op.value_}; } -bool YGFloatOptional::operator>(const YGFloatOptional& op) const { - if (isUndefined_ || op.isUndefined_) { - return false; - } +bool YGFloatOptional::operator>(YGFloatOptional op) const { return value_ > op.value_; } -bool YGFloatOptional::operator<(const YGFloatOptional& op) const { - if (isUndefined_ || op.isUndefined_) { - return false; - } +bool YGFloatOptional::operator<(YGFloatOptional op) const { return value_ < op.value_; } -bool YGFloatOptional::operator>=(const YGFloatOptional& op) const { - return *this == op || *this > op; +bool YGFloatOptional::operator>=(YGFloatOptional op) const { + return *this > op || *this == op; } -bool YGFloatOptional::operator<=(const YGFloatOptional& op) const { - return *this == op || *this < op; +bool YGFloatOptional::operator<=(YGFloatOptional op) const { + return *this < op || *this == op; } diff --git a/yoga/YGFloatOptional.h b/yoga/YGFloatOptional.h index 07fbb64b..7b573d38 100644 --- a/yoga/YGFloatOptional.h +++ b/yoga/YGFloatOptional.h @@ -6,37 +6,33 @@ */ #pragma once +#include +#include + struct YGFloatOptional { private: - float value_ = 0; - bool isUndefined_ = true; + float value_ = std::numeric_limits::quiet_NaN(); public: - explicit YGFloatOptional(float value); - YGFloatOptional() = default; + explicit constexpr YGFloatOptional(float value) : value_(value) {} + constexpr YGFloatOptional() = default; // Program will terminate if the value of an undefined is accessed. Please // make sure to check if the optional is defined before calling this function. // To check if float optional is defined, use `isUndefined()`. float getValue() const; - // Sets the value of float optional, and thus isUndefined is assigned false. - void setValue(float val) { - value_ = val; - isUndefined_ = false; - } - bool isUndefined() const { - return isUndefined_; + return std::isnan(value_); } - YGFloatOptional operator+(const YGFloatOptional& op); - bool operator>(const YGFloatOptional& op) const; - bool operator<(const YGFloatOptional& op) const; - bool operator>=(const YGFloatOptional& op) const; - bool operator<=(const YGFloatOptional& op) const; - bool operator==(const YGFloatOptional& op) const; - bool operator!=(const YGFloatOptional& op) const; + YGFloatOptional operator+(YGFloatOptional op) const; + bool operator>(YGFloatOptional op) const; + bool operator<(YGFloatOptional op) const; + bool operator>=(YGFloatOptional op) const; + bool operator<=(YGFloatOptional op) const; + bool operator==(YGFloatOptional op) const; + bool operator!=(YGFloatOptional op) const; bool operator==(float val) const; bool operator!=(float val) const; diff --git a/yoga/YGNode.cpp b/yoga/YGNode.cpp index 1694692c..04c3c7f6 100644 --- a/yoga/YGNode.cpp +++ b/yoga/YGNode.cpp @@ -211,7 +211,7 @@ YGFloatOptional YGNode::relativePosition( YGFloatOptional trailingPosition = getTrailingPosition(axis, axisSize); if (!trailingPosition.isUndefined()) { - trailingPosition.setValue(-1 * trailingPosition.getValue()); + trailingPosition = YGFloatOptional{-1 * trailingPosition.getValue()}; } return trailingPosition; }