Skip to content

Commit

Permalink
C++ Cleanup 7/N: BitUtils
Browse files Browse the repository at this point in the history
Summary:
X-link: facebook/yoga#1351

## This diff

This splits up `BitUtils.h`, does some minor renaming, and namespace consistency fixes.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48768374

fbshipit-source-id: 2d206c5e51799fbf0d83462f069a97addd1eb381
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Aug 30, 2023
1 parent 8db1fa2 commit 1de79ff
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 64 deletions.
19 changes: 19 additions & 0 deletions packages/react-native/ReactCommon/yoga/yoga/bits/EnumBitset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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 <bitset>
#include <yoga/YGEnums.h>

namespace facebook::yoga {

// std::bitset with one bit for each option defined in YG_ENUM_SEQ_DECL
template <typename Enum>
using EnumBitset = std::bitset<enums::count<Enum>()>;

} // namespace facebook::yoga
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@

#include <yoga/YGEnums.h>

namespace facebook::yoga::detail {

// std::bitset with one bit for each option defined in YG_ENUM_SEQ_DECL
template <typename Enum>
using EnumBitset = std::bitset<facebook::yoga::enums::count<Enum>()>;
namespace facebook::yoga::details {

constexpr size_t log2ceilFn(size_t n) {
return n < 1 ? 0 : (1 + log2ceilFn(n / 2));
Expand All @@ -27,30 +23,37 @@ constexpr int mask(size_t bitWidth, size_t index) {
return ((1 << bitWidth) - 1) << index;
}

} // namespace facebook::yoga::details

namespace facebook::yoga {

// The number of bits necessary to represent enums defined with YG_ENUM_SEQ_DECL
template <typename Enum>
constexpr size_t bitWidthFn() {
constexpr size_t minimumBitCount() {
static_assert(
enums::count<Enum>() > 0, "Enums must have at least one entries");
return log2ceilFn(enums::count<Enum>() - 1);
return details::log2ceilFn(enums::count<Enum>() - 1);
}

template <typename Enum>
constexpr Enum getEnumData(int flags, size_t index) {
return static_cast<Enum>((flags & mask(bitWidthFn<Enum>(), index)) >> index);
return static_cast<Enum>(
(flags & details::mask(minimumBitCount<Enum>(), index)) >> index);
}

template <typename Enum>
void setEnumData(uint32_t& flags, size_t index, int newValue) {
flags = (flags & ~mask(bitWidthFn<Enum>(), index)) |
((newValue << index) & (mask(bitWidthFn<Enum>(), index)));
flags = (flags & ~details::mask(minimumBitCount<Enum>(), index)) |
((newValue << index) & (details::mask(minimumBitCount<Enum>(), index)));
}

template <typename Enum>
void setEnumData(uint8_t& flags, size_t index, int newValue) {
flags = (flags & ~static_cast<uint8_t>(mask(bitWidthFn<Enum>(), index))) |
flags =
(flags &
~static_cast<uint8_t>(details::mask(minimumBitCount<Enum>(), index))) |
((newValue << index) &
(static_cast<uint8_t>(mask(bitWidthFn<Enum>(), index))));
(static_cast<uint8_t>(details::mask(minimumBitCount<Enum>(), index))));
}

constexpr bool getBooleanData(int flags, size_t index) {
Expand All @@ -65,4 +68,4 @@ inline void setBooleanData(uint8_t& flags, size_t index, bool value) {
}
}

} // namespace facebook::yoga::detail
} // namespace facebook::yoga
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ bool Config::isExperimentalFeatureEnabled(YGExperimentalFeature feature) const {
return experimentalFeatures_.test(feature);
}

ExperimentalFeatureSet Config::getEnabledExperiments() const {
EnumBitset<YGExperimentalFeature> Config::getEnabledExperiments() const {
return experimentalFeatures_;
}

Expand Down
8 changes: 3 additions & 5 deletions packages/react-native/ReactCommon/yoga/yoga/config/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#include <yoga/Yoga.h>

#include <yoga/BitUtils.h>
#include <yoga/bits/EnumBitset.h>
#include <yoga/Yoga-internal.h>

// Tag struct used to form the opaque YGConfigRef for the public C API
Expand Down Expand Up @@ -38,8 +38,6 @@ using CloneWithContextFn = YGNodeRef (*)(
int childIndex,
void* cloneContext);

using ExperimentalFeatureSet = detail::EnumBitset<YGExperimentalFeature>;

#pragma pack(push)
#pragma pack(1)
// Packed structure of <32-bit options to miminize size per node.
Expand All @@ -65,7 +63,7 @@ class YOGA_EXPORT Config : public ::YGConfig {
YGExperimentalFeature feature,
bool enabled);
bool isExperimentalFeatureEnabled(YGExperimentalFeature feature) const;
ExperimentalFeatureSet getEnabledExperiments() const;
EnumBitset<YGExperimentalFeature> getEnabledExperiments() const;

void setErrata(YGErrata errata);
void addErrata(YGErrata errata);
Expand Down Expand Up @@ -104,7 +102,7 @@ class YOGA_EXPORT Config : public ::YGConfig {
} logger_;

ConfigFlags flags_{};
ExperimentalFeatureSet experimentalFeatures_{};
EnumBitset<YGExperimentalFeature> experimentalFeatures_{};
YGErrata errata_ = YGErrataNone;
float pointScaleFactor_ = 1.0f;
void* context_ = nullptr;
Expand Down
17 changes: 6 additions & 11 deletions packages/react-native/ReactCommon/yoga/yoga/node/LayoutResults.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#pragma once

#include <yoga/BitUtils.h>
#include <yoga/bits/NumericBitfield.h>
#include <yoga/numeric/FloatOptional.h>
#include <yoga/Yoga-internal.h>

Expand All @@ -23,7 +23,7 @@ struct LayoutResults {
private:
static constexpr size_t directionOffset = 0;
static constexpr size_t hadOverflowOffset =
directionOffset + facebook::yoga::detail::bitWidthFn<YGDirection>();
directionOffset + minimumBitCount<YGDirection>();
uint8_t flags = 0;

public:
Expand All @@ -43,21 +43,16 @@ struct LayoutResults {
YGCachedMeasurement cachedLayout = YGCachedMeasurement();

YGDirection direction() const {
return facebook::yoga::detail::getEnumData<YGDirection>(
flags, directionOffset);
return getEnumData<YGDirection>(flags, directionOffset);
}

void setDirection(YGDirection direction) {
facebook::yoga::detail::setEnumData<YGDirection>(
flags, directionOffset, direction);
setEnumData<YGDirection>(flags, directionOffset, direction);
}

bool hadOverflow() const {
return facebook::yoga::detail::getBooleanData(flags, hadOverflowOffset);
}
bool hadOverflow() const { return getBooleanData(flags, hadOverflowOffset); }
void setHadOverflow(bool hadOverflow) {
facebook::yoga::detail::setBooleanData(
flags, hadOverflowOffset, hadOverflow);
setBooleanData(flags, hadOverflowOffset, hadOverflow);
}

bool operator==(LayoutResults layout) const;
Expand Down
57 changes: 23 additions & 34 deletions packages/react-native/ReactCommon/yoga/yoga/style/Style.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

#include <yoga/Yoga.h>
#include <yoga/Yoga-internal.h>
#include <yoga/BitUtils.h>

#include <yoga/bits/NumericBitfield.h>
#include <yoga/numeric/FloatOptional.h>
#include <yoga/style/CompactValue.h>

Expand All @@ -34,11 +34,9 @@ class YOGA_EXPORT Style {
struct BitfieldRef {
Style& style;
size_t offset;
operator T() const {
return facebook::yoga::detail::getEnumData<T>(style.flags, offset);
}
operator T() const { return getEnumData<T>(style.flags, offset); }
BitfieldRef<T>& operator=(T x) {
facebook::yoga::detail::setEnumData<T>(style.flags, offset, x);
setEnumData<T>(style.flags, offset, x);
return *this;
}
};
Expand Down Expand Up @@ -85,23 +83,23 @@ class YOGA_EXPORT Style {
private:
static constexpr size_t directionOffset = 0;
static constexpr size_t flexdirectionOffset =
directionOffset + facebook::yoga::detail::bitWidthFn<YGDirection>();
static constexpr size_t justifyContentOffset = flexdirectionOffset +
facebook::yoga::detail::bitWidthFn<YGFlexDirection>();
directionOffset + minimumBitCount<YGDirection>();
static constexpr size_t justifyContentOffset =
flexdirectionOffset + minimumBitCount<YGFlexDirection>();
static constexpr size_t alignContentOffset =
justifyContentOffset + facebook::yoga::detail::bitWidthFn<YGJustify>();
justifyContentOffset + minimumBitCount<YGJustify>();
static constexpr size_t alignItemsOffset =
alignContentOffset + facebook::yoga::detail::bitWidthFn<YGAlign>();
alignContentOffset + minimumBitCount<YGAlign>();
static constexpr size_t alignSelfOffset =
alignItemsOffset + facebook::yoga::detail::bitWidthFn<YGAlign>();
alignItemsOffset + minimumBitCount<YGAlign>();
static constexpr size_t positionTypeOffset =
alignSelfOffset + facebook::yoga::detail::bitWidthFn<YGAlign>();
alignSelfOffset + minimumBitCount<YGAlign>();
static constexpr size_t flexWrapOffset =
positionTypeOffset + facebook::yoga::detail::bitWidthFn<YGPositionType>();
positionTypeOffset + minimumBitCount<YGPositionType>();
static constexpr size_t overflowOffset =
flexWrapOffset + facebook::yoga::detail::bitWidthFn<YGWrap>();
flexWrapOffset + minimumBitCount<YGWrap>();
static constexpr size_t displayOffset =
overflowOffset + facebook::yoga::detail::bitWidthFn<YGOverflow>();
overflowOffset + minimumBitCount<YGOverflow>();

uint32_t flags = 0;

Expand All @@ -125,65 +123,56 @@ class YOGA_EXPORT Style {
using ValueRepr = std::remove_reference<decltype(margin_[0])>::type;

YGDirection direction() const {
return facebook::yoga::detail::getEnumData<YGDirection>(
flags, directionOffset);
return getEnumData<YGDirection>(flags, directionOffset);
}
BitfieldRef<YGDirection> direction() { return {*this, directionOffset}; }

YGFlexDirection flexDirection() const {
return facebook::yoga::detail::getEnumData<YGFlexDirection>(
flags, flexdirectionOffset);
return getEnumData<YGFlexDirection>(flags, flexdirectionOffset);
}
BitfieldRef<YGFlexDirection> flexDirection() {
return {*this, flexdirectionOffset};
}

YGJustify justifyContent() const {
return facebook::yoga::detail::getEnumData<YGJustify>(
flags, justifyContentOffset);
return getEnumData<YGJustify>(flags, justifyContentOffset);
}
BitfieldRef<YGJustify> justifyContent() {
return {*this, justifyContentOffset};
}

YGAlign alignContent() const {
return facebook::yoga::detail::getEnumData<YGAlign>(
flags, alignContentOffset);
return getEnumData<YGAlign>(flags, alignContentOffset);
}
BitfieldRef<YGAlign> alignContent() { return {*this, alignContentOffset}; }

YGAlign alignItems() const {
return facebook::yoga::detail::getEnumData<YGAlign>(
flags, alignItemsOffset);
return getEnumData<YGAlign>(flags, alignItemsOffset);
}
BitfieldRef<YGAlign> alignItems() { return {*this, alignItemsOffset}; }

YGAlign alignSelf() const {
return facebook::yoga::detail::getEnumData<YGAlign>(flags, alignSelfOffset);
return getEnumData<YGAlign>(flags, alignSelfOffset);
}
BitfieldRef<YGAlign> alignSelf() { return {*this, alignSelfOffset}; }

YGPositionType positionType() const {
return facebook::yoga::detail::getEnumData<YGPositionType>(
flags, positionTypeOffset);
return getEnumData<YGPositionType>(flags, positionTypeOffset);
}
BitfieldRef<YGPositionType> positionType() {
return {*this, positionTypeOffset};
}

YGWrap flexWrap() const {
return facebook::yoga::detail::getEnumData<YGWrap>(flags, flexWrapOffset);
}
YGWrap flexWrap() const { return getEnumData<YGWrap>(flags, flexWrapOffset); }
BitfieldRef<YGWrap> flexWrap() { return {*this, flexWrapOffset}; }

YGOverflow overflow() const {
return facebook::yoga::detail::getEnumData<YGOverflow>(
flags, overflowOffset);
return getEnumData<YGOverflow>(flags, overflowOffset);
}
BitfieldRef<YGOverflow> overflow() { return {*this, overflowOffset}; }

YGDisplay display() const {
return facebook::yoga::detail::getEnumData<YGDisplay>(flags, displayOffset);
return getEnumData<YGDisplay>(flags, displayOffset);
}
BitfieldRef<YGDisplay> display() { return {*this, displayOffset}; }

Expand Down

0 comments on commit 1de79ff

Please sign in to comment.