Skip to content

Commit

Permalink
C++ Cleanup 8/N: Yoga-internal (facebook#1355)
Browse files Browse the repository at this point in the history
Summary:

X-link: facebook/react-native#39198

## This diff

This splits up `Yoga-internal.h` which has become a grab bag. The actual header is left, with the purpose of being a private C ABI for bindings, but everything else is moved to a place more appropriate or removed.

A few notes:
1. `yoga::isUndefined` is replaced with `std::isnan` to avoid a layer of indirection (we will never be able to change its representation anyway). Internal usages of `YGFloatIsUndefined` are also replaced with `std::isnan` since the previous being at a library boundary means I'm not sure it can be inlined/.
2. `leading`, `trailing` arrays are factored into proper functions
3. `Values` is replaced entirely with `std::array`, since most of it was unused.

## 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: D48769241
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Sep 4, 2023
1 parent 31b6c0d commit 8b612da
Show file tree
Hide file tree
Showing 15 changed files with 249 additions and 256 deletions.
2 changes: 2 additions & 0 deletions java/jni/YGJNIVanilla.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <memory>
#include "YogaJniException.h"

#include <yoga/Yoga-internal.h>

// TODO: Reconcile missing layoutContext functionality from callbacks in the C
// API and use that
#include <yoga/node/Node.h>
Expand Down
120 changes: 0 additions & 120 deletions yoga/Yoga-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@
#pragma once

#include <algorithm>
#include <array>
#include <cmath>
#include <vector>

#include <yoga/Yoga.h>

#include <yoga/style/CompactValue.h>

YG_EXTERN_C_BEGIN

void YGNodeCalculateLayoutWithContext(
Expand All @@ -30,120 +27,3 @@ void YGNodeCalculateLayoutWithContext(
void YGNodeDeallocate(YGNodeRef node);

YG_EXTERN_C_END

namespace facebook::yoga {

inline bool isUndefined(float value) {
return std::isnan(value);
}

inline bool isUndefined(double value) {
return std::isnan(value);
}

} // namespace facebook::yoga

extern const std::array<YGEdge, 4> trailing;
extern const std::array<YGEdge, 4> leading;
extern const YGValue YGValueUndefined;
extern const YGValue YGValueAuto;
extern const YGValue YGValueZero;

struct YGCachedMeasurement {
float availableWidth;
float availableHeight;
YGMeasureMode widthMeasureMode;
YGMeasureMode heightMeasureMode;

float computedWidth;
float computedHeight;

YGCachedMeasurement()
: availableWidth(-1),
availableHeight(-1),
widthMeasureMode(YGMeasureModeUndefined),
heightMeasureMode(YGMeasureModeUndefined),
computedWidth(-1),
computedHeight(-1) {}

bool operator==(YGCachedMeasurement measurement) const {
using namespace facebook;

bool isEqual = widthMeasureMode == measurement.widthMeasureMode &&
heightMeasureMode == measurement.heightMeasureMode;

if (!yoga::isUndefined(availableWidth) ||
!yoga::isUndefined(measurement.availableWidth)) {
isEqual = isEqual && availableWidth == measurement.availableWidth;
}
if (!yoga::isUndefined(availableHeight) ||
!yoga::isUndefined(measurement.availableHeight)) {
isEqual = isEqual && availableHeight == measurement.availableHeight;
}
if (!yoga::isUndefined(computedWidth) ||
!yoga::isUndefined(measurement.computedWidth)) {
isEqual = isEqual && computedWidth == measurement.computedWidth;
}
if (!yoga::isUndefined(computedHeight) ||
!yoga::isUndefined(measurement.computedHeight)) {
isEqual = isEqual && computedHeight == measurement.computedHeight;
}

return isEqual;
}
};

// This value was chosen based on empirical data:
// 98% of analyzed layouts require less than 8 entries.
#define YG_MAX_CACHED_RESULT_COUNT 8

namespace facebook::yoga::detail {

template <size_t Size>
class Values {
private:
std::array<CompactValue, Size> values_;

public:
Values() = default;
Values(const Values& other) = default;

explicit Values(const YGValue& defaultValue) noexcept {
values_.fill(defaultValue);
}

const CompactValue& operator[](size_t i) const noexcept { return values_[i]; }
CompactValue& operator[](size_t i) noexcept { return values_[i]; }

template <size_t I>
YGValue get() const noexcept {
return std::get<I>(values_);
}

template <size_t I>
void set(YGValue& value) noexcept {
std::get<I>(values_) = value;
}

template <size_t I>
void set(YGValue&& value) noexcept {
set<I>(value);
}

bool operator==(const Values& other) const noexcept {
for (size_t i = 0; i < Size; ++i) {
if (values_[i] != other.values_[i]) {
return false;
}
}
return true;
}

Values& operator=(const Values& other) = default;
};

} // namespace facebook::yoga::detail

static const float kDefaultFlexGrow = 0.0f;
static const float kDefaultFlexShrink = 0.0f;
static const float kWebDefaultFlexShrink = 1.0f;
Loading

0 comments on commit 8b612da

Please sign in to comment.