Track which style properties have been set on YGStyle

Summary:
@public

In order to optimise property storage, we have to know how style properties are used in our apps.
Here, we add a bitmask that allows us to track which properties are set explicitely, and use that for our analysis.

Reviewed By: SidharthGuglani

Differential Revision: D14933022

fbshipit-source-id: 1ab8af562b14baba1d02057e527aa36d5c9a7823
This commit is contained in:
David Aurelio
2019-05-01 06:47:31 -07:00
committed by Facebook Github Bot
parent 05d205cf89
commit 011c1964a0
2 changed files with 151 additions and 31 deletions

View File

@@ -4,15 +4,29 @@
* This source code is licensed under the MIT license found in the LICENSE * This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree. * file in the root directory of this source tree.
*/ */
#include <cstdint>
#include <type_traits>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <yoga/YGEnums.h>
#include <yoga/YGStyle.h> #include <yoga/YGStyle.h>
#include <yoga/YGValue.h> #include <yoga/YGValue.h>
#include <utility> #include <utility>
using AssignedProps =
std::remove_reference<decltype(YGStyle{}.assignedProps())>::type;
namespace {
constexpr AssignedProps setBits(int from, int n) {
return n > 0 ? (setBits(from, n - 1) | AssignedProps{1ull << (from + n - 1)})
: 0;
}
} // namespace
#define ACCESSOR_TESTS_1(NAME, X) \ #define ACCESSOR_TESTS_1(NAME, X) \
style.NAME() = X; \ style.NAME() = X; \
ASSERT_EQ(style.NAME(), X); ASSERT_EQ(style.NAME(), X); \
ASSERT_EQ(style.assignedProps(), AssignedProps{1ull << YGStyle::NAME##Bit});
#define ACCESSOR_TESTS_2(NAME, X, ...) \ #define ACCESSOR_TESTS_2(NAME, X, ...) \
ACCESSOR_TESTS_1(NAME, X); \ ACCESSOR_TESTS_1(NAME, X); \
ACCESSOR_TESTS_1(NAME, __VA_ARGS__); ACCESSOR_TESTS_1(NAME, __VA_ARGS__);
@@ -31,11 +45,20 @@
#define INDEX_ACCESSOR_TESTS_1(NAME, IDX, X) \ #define INDEX_ACCESSOR_TESTS_1(NAME, IDX, X) \
{ \ { \
auto style = YGStyle{}; \
style.NAME()[IDX] = X; \ style.NAME()[IDX] = X; \
ASSERT_EQ(style.NAME()[IDX], X); \ ASSERT_EQ(style.NAME()[IDX], X); \
ASSERT_EQ( \
style.assignedProps(), \
AssignedProps{1ull << (YGStyle::NAME##Bit + IDX)}); \
auto asArray = decltype(std::declval<const YGStyle&>().NAME()){X}; \ auto asArray = decltype(std::declval<const YGStyle&>().NAME()){X}; \
style.NAME() = asArray; \ style.NAME() = asArray; \
ASSERT_EQ(static_cast<decltype(asArray)>(style.NAME()), asArray); \ ASSERT_EQ(static_cast<decltype(asArray)>(style.NAME()), asArray); \
ASSERT_EQ( \
style.assignedProps(), \
AssignedProps{setBits( \
YGStyle::NAME##Bit, \
facebook::yoga::enums::count<decltype(IDX)>())}); \
} }
#define INDEX_ACCESSOR_TESTS_2(NAME, IDX, X, Y) \ #define INDEX_ACCESSOR_TESTS_2(NAME, IDX, X, Y) \
@@ -69,8 +92,8 @@
#define INDEX_ACCESSOR_TEST(NAME, DEFAULT_VAL, IDX, ...) \ #define INDEX_ACCESSOR_TEST(NAME, DEFAULT_VAL, IDX, ...) \
TEST(YGStyle, style_##NAME##_access) { \ TEST(YGStyle, style_##NAME##_access) { \
auto style = YGStyle{}; \ ASSERT_EQ(YGStyle{}.NAME()[IDX], DEFAULT_VAL); \
ASSERT_EQ(style.NAME()[IDX], DEFAULT_VAL); \ ASSERT_EQ(YGStyle{}.assignedProps(), 0); \
INDEX_ACCESSOR_TESTS(__VA_ARGS__)(NAME, IDX, __VA_ARGS__) \ INDEX_ACCESSOR_TESTS(__VA_ARGS__)(NAME, IDX, __VA_ARGS__) \
} }
@@ -247,5 +270,29 @@ ACCESSOR_TEST(
YGFloatOptional{0.0f}, YGFloatOptional{0.0f},
YGFloatOptional{}); YGFloatOptional{});
TEST(YGStyle, set_properties_default_to_0) {
ASSERT_EQ(YGStyle{}.assignedProps(), AssignedProps{0});
}
TEST(YGStyle, set_properties_reflects_all_set_properties) {
auto style = YGStyle{};
style.direction() = YGDirectionRTL;
style.justifyContent() = YGJustifySpaceAround;
style.flexWrap() = YGWrapWrap;
style.padding()[YGEdgeVertical] = YGValue{1, YGUnitPoint};
style.minDimensions()[YGDimensionHeight] = YGValue{1, YGUnitPercent};
style.aspectRatio() = YGFloatOptional{1.23};
ASSERT_EQ(
style.assignedProps(),
AssignedProps{1ull << YGStyle::directionBit |
1ull << YGStyle::justifyContentBit |
1ull << YGStyle::flexWrapBit |
1ull << (YGStyle::paddingBit + YGEdgeVertical) |
1ull << (YGStyle::minDimensionsBit + YGDimensionHeight) |
1ull << YGStyle::aspectRatioBit});
}
} // namespace yoga } // namespace yoga
} // namespace facebook } // namespace facebook

View File

@@ -7,6 +7,8 @@
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <bitset>
#include <cstdint>
#include <type_traits> #include <type_traits>
#include "CompactValue.h" #include "CompactValue.h"
#include "YGEnums.h" #include "YGEnums.h"
@@ -33,21 +35,28 @@ class YGStyle {
facebook::yoga::detail::Values<facebook::yoga::enums::count<Enum>()>; facebook::yoga::detail::Values<facebook::yoga::enums::count<Enum>()>;
using CompactValue = facebook::yoga::detail::CompactValue; using CompactValue = facebook::yoga::detail::CompactValue;
static constexpr uint64_t allBits(int fromBit, int toBit) {
return fromBit < toBit
? (uint64_t{1} << fromBit) | allBits(fromBit + 1, toBit)
: 0;
}
public: public:
using Dimensions = Values<YGDimension>; using Dimensions = Values<YGDimension>;
using Edges = Values<YGEdge>; using Edges = Values<YGEdge>;
template <typename T, T YGStyle::*Prop> template <typename T, T YGStyle::*Prop, int PropBit>
struct Ref { struct Ref {
YGStyle& style; YGStyle& style;
operator T() const { return style.*Prop; } operator T() const { return style.*Prop; }
Ref<T, Prop>& operator=(T value) { Ref<T, Prop, PropBit>& operator=(T value) {
style.*Prop = value; style.*Prop = value;
style.assignedProps_.set(PropBit);
return *this; return *this;
} }
}; };
template <typename Idx, Values<Idx> YGStyle::*Prop> template <typename Idx, Values<Idx> YGStyle::*Prop, int PropBit>
struct IdxRef { struct IdxRef {
struct Ref { struct Ref {
YGStyle& style; YGStyle& style;
@@ -56,13 +65,16 @@ public:
operator YGValue() const { return (style.*Prop)[idx]; } operator YGValue() const { return (style.*Prop)[idx]; }
Ref& operator=(CompactValue value) { Ref& operator=(CompactValue value) {
(style.*Prop)[idx] = value; (style.*Prop)[idx] = value;
style.assignedProps_.set(PropBit + idx);
return *this; return *this;
} }
}; };
YGStyle& style; YGStyle& style;
IdxRef<Idx, Prop>& operator=(const Values<Idx>& values) { IdxRef<Idx, Prop, PropBit>& operator=(const Values<Idx>& values) {
style.*Prop = values; style.*Prop = values;
style.assignedProps_ |=
allBits(PropBit, PropBit + facebook::yoga::enums::count<Idx>());
return *this; return *this;
} }
operator const Values<Idx>&() const { return style.*Prop; } operator const Values<Idx>&() const { return style.*Prop; }
@@ -70,15 +82,16 @@ public:
CompactValue operator[](Idx idx) const { return (style.*Prop)[idx]; } CompactValue operator[](Idx idx) const { return (style.*Prop)[idx]; }
}; };
template <typename T> template <typename T, int PropBit>
struct BitfieldRef { struct BitfieldRef {
YGStyle& style; YGStyle& style;
T (YGStyle::*get)() const; T (YGStyle::*get)() const;
void (YGStyle::*set)(T); void (YGStyle::*set)(T);
operator T() const { return (style.*get)(); } operator T() const { return (style.*get)(); }
BitfieldRef<T>& operator=(T x) { BitfieldRef<T, PropBit>& operator=(T x) {
(style.*set)(x); (style.*set)(x);
style.assignedProps_.set(PropBit);
return *this; return *this;
} }
}; };
@@ -96,7 +109,39 @@ public:
display_(YGDisplayFlex) {} display_(YGDisplayFlex) {}
~YGStyle() = default; ~YGStyle() = default;
static constexpr int directionBit = 0;
static constexpr int flexDirectionBit = directionBit + 1;
static constexpr int justifyContentBit = flexDirectionBit + 1;
static constexpr int alignContentBit = justifyContentBit + 1;
static constexpr int alignItemsBit = alignContentBit + 1;
static constexpr int alignSelfBit = alignItemsBit + 1;
static constexpr int positionTypeBit = alignSelfBit + 1;
static constexpr int flexWrapBit = positionTypeBit + 1;
static constexpr int overflowBit = flexWrapBit + 1;
static constexpr int displayBit = overflowBit + 1;
static constexpr int flexBit = displayBit + 1;
static constexpr int flexGrowBit = flexBit + 1;
static constexpr int flexShrinkBit = flexGrowBit + 1;
static constexpr int flexBasisBit = flexShrinkBit + 1;
static constexpr int marginBit = flexBasisBit + 1;
static constexpr int positionBit =
marginBit + facebook::yoga::enums::count<YGEdge>();
static constexpr int paddingBit =
positionBit + facebook::yoga::enums::count<YGEdge>();
static constexpr int borderBit =
paddingBit + facebook::yoga::enums::count<YGEdge>();
static constexpr int dimensionsBit =
borderBit + facebook::yoga::enums::count<YGDimension>();
static constexpr int maxDimensionsBit =
dimensionsBit + facebook::yoga::enums::count<YGDimension>();
static constexpr int minDimensionsBit =
maxDimensionsBit + facebook::yoga::enums::count<YGDimension>();
static constexpr int aspectRatioBit =
minDimensionsBit + facebook::yoga::enums::count<YGDimension>();
private: private:
std::bitset<aspectRatioBit + 1> assignedProps_;
/* Some platforms don't support enum bitfields, /* Some platforms don't support enum bitfields,
so please use BITFIELD_ENUM_SIZED(BITS_COUNT) */ so please use BITFIELD_ENUM_SIZED(BITS_COUNT) */
YGDirection direction_ BITFIELD_ENUM_SIZED(2); YGDirection direction_ BITFIELD_ENUM_SIZED(2);
@@ -124,85 +169,113 @@ private:
YGFloatOptional aspectRatio_ = {}; YGFloatOptional aspectRatio_ = {};
public: public:
const decltype(assignedProps_)& assignedProps() const {
return assignedProps_;
}
// for library users needing a type // for library users needing a type
using ValueRepr = std::remove_reference<decltype(margin_[0])>::type; using ValueRepr = std::remove_reference<decltype(margin_[0])>::type;
YGDirection direction() const { return direction_; } YGDirection direction() const { return direction_; }
BitfieldRef<YGDirection> direction() { return BITFIELD_REF(direction); } BitfieldRef<YGDirection, directionBit> direction() {
return BITFIELD_REF(direction);
}
YGFlexDirection flexDirection() const { return flexDirection_; } YGFlexDirection flexDirection() const { return flexDirection_; }
BitfieldRef<YGFlexDirection> flexDirection() { BitfieldRef<YGFlexDirection, flexDirectionBit> flexDirection() {
return BITFIELD_REF(flexDirection); return BITFIELD_REF(flexDirection);
} }
YGJustify justifyContent() const { return justifyContent_; } YGJustify justifyContent() const { return justifyContent_; }
BitfieldRef<YGJustify> justifyContent() { BitfieldRef<YGJustify, justifyContentBit> justifyContent() {
return BITFIELD_REF(justifyContent); return BITFIELD_REF(justifyContent);
} }
YGAlign alignContent() const { return alignContent_; } YGAlign alignContent() const { return alignContent_; }
BitfieldRef<YGAlign> alignContent() { return BITFIELD_REF(alignContent); } BitfieldRef<YGAlign, alignContentBit> alignContent() {
return BITFIELD_REF(alignContent);
}
YGAlign alignItems() const { return alignItems_; } YGAlign alignItems() const { return alignItems_; }
BitfieldRef<YGAlign> alignItems() { return BITFIELD_REF(alignItems); } BitfieldRef<YGAlign, alignItemsBit> alignItems() {
return BITFIELD_REF(alignItems);
}
YGAlign alignSelf() const { return alignSelf_; } YGAlign alignSelf() const { return alignSelf_; }
BitfieldRef<YGAlign> alignSelf() { return BITFIELD_REF(alignSelf); } BitfieldRef<YGAlign, alignSelfBit> alignSelf() {
return BITFIELD_REF(alignSelf);
}
YGPositionType positionType() const { return positionType_; } YGPositionType positionType() const { return positionType_; }
BitfieldRef<YGPositionType> positionType() { BitfieldRef<YGPositionType, positionTypeBit> positionType() {
return BITFIELD_REF(positionType); return BITFIELD_REF(positionType);
} }
YGWrap flexWrap() const { return flexWrap_; } YGWrap flexWrap() const { return flexWrap_; }
BitfieldRef<YGWrap> flexWrap() { return BITFIELD_REF(flexWrap); } BitfieldRef<YGWrap, flexWrapBit> flexWrap() { return BITFIELD_REF(flexWrap); }
YGOverflow overflow() const { return overflow_; } YGOverflow overflow() const { return overflow_; }
BitfieldRef<YGOverflow> overflow() { return BITFIELD_REF(overflow); } BitfieldRef<YGOverflow, overflowBit> overflow() {
return BITFIELD_REF(overflow);
}
YGDisplay display() const { return display_; } YGDisplay display() const { return display_; }
BitfieldRef<YGDisplay> display() { return BITFIELD_REF(display); } BitfieldRef<YGDisplay, displayBit> display() { return BITFIELD_REF(display); }
YGFloatOptional flex() const { return flex_; } YGFloatOptional flex() const { return flex_; }
Ref<YGFloatOptional, &YGStyle::flex_> flex() { return {*this}; } Ref<YGFloatOptional, &YGStyle::flex_, flexBit> flex() { return {*this}; }
YGFloatOptional flexGrow() const { return flexGrow_; } YGFloatOptional flexGrow() const { return flexGrow_; }
Ref<YGFloatOptional, &YGStyle::flexGrow_> flexGrow() { return {*this}; } Ref<YGFloatOptional, &YGStyle::flexGrow_, flexGrowBit> flexGrow() {
return {*this};
}
YGFloatOptional flexShrink() const { return flexShrink_; } YGFloatOptional flexShrink() const { return flexShrink_; }
Ref<YGFloatOptional, &YGStyle::flexShrink_> flexShrink() { return {*this}; } Ref<YGFloatOptional, &YGStyle::flexShrink_, flexShrinkBit> flexShrink() {
return {*this};
}
CompactValue flexBasis() const { return flexBasis_; } CompactValue flexBasis() const { return flexBasis_; }
Ref<CompactValue, &YGStyle::flexBasis_> flexBasis() { return {*this}; } Ref<CompactValue, &YGStyle::flexBasis_, flexBasisBit> flexBasis() {
return {*this};
}
const Edges& margin() const { return margin_; } const Edges& margin() const { return margin_; }
IdxRef<YGEdge, &YGStyle::margin_> margin() { return {*this}; } IdxRef<YGEdge, &YGStyle::margin_, marginBit> margin() { return {*this}; }
const Edges& position() const { return position_; } const Edges& position() const { return position_; }
IdxRef<YGEdge, &YGStyle::position_> position() { return {*this}; } IdxRef<YGEdge, &YGStyle::position_, positionBit> position() {
return {*this};
}
const Edges& padding() const { return padding_; } const Edges& padding() const { return padding_; }
IdxRef<YGEdge, &YGStyle::padding_> padding() { return {*this}; } IdxRef<YGEdge, &YGStyle::padding_, paddingBit> padding() { return {*this}; }
const Edges& border() const { return border_; } const Edges& border() const { return border_; }
IdxRef<YGEdge, &YGStyle::border_> border() { return {*this}; } IdxRef<YGEdge, &YGStyle::border_, borderBit> border() { return {*this}; }
const Dimensions& dimensions() const { return dimensions_; } const Dimensions& dimensions() const { return dimensions_; }
IdxRef<YGDimension, &YGStyle::dimensions_> dimensions() { return {*this}; } IdxRef<YGDimension, &YGStyle::dimensions_, dimensionsBit> dimensions() {
return {*this};
}
const Dimensions& minDimensions() const { return minDimensions_; } const Dimensions& minDimensions() const { return minDimensions_; }
IdxRef<YGDimension, &YGStyle::minDimensions_> minDimensions() { IdxRef<YGDimension, &YGStyle::minDimensions_, minDimensionsBit>
minDimensions() {
return {*this}; return {*this};
} }
const Dimensions& maxDimensions() const { return maxDimensions_; } const Dimensions& maxDimensions() const { return maxDimensions_; }
IdxRef<YGDimension, &YGStyle::maxDimensions_> maxDimensions() { IdxRef<YGDimension, &YGStyle::maxDimensions_, maxDimensionsBit>
maxDimensions() {
return {*this}; return {*this};
} }
// Yoga specific properties, not compatible with flexbox specification // Yoga specific properties, not compatible with flexbox specification
YGFloatOptional aspectRatio() const { return aspectRatio_; } YGFloatOptional aspectRatio() const { return aspectRatio_; }
Ref<YGFloatOptional, &YGStyle::aspectRatio_> aspectRatio() { return {*this}; } Ref<YGFloatOptional, &YGStyle::aspectRatio_, aspectRatioBit> aspectRatio() {
return {*this};
}
private: private:
BITFIELD_ACCESSORS(direction) BITFIELD_ACCESSORS(direction)