Skip to content

Commit b65866c

Browse files
committed
guard shrink-factor division against floating-point near-zero
1 parent 4a59d09 commit b65866c

2 files changed

Lines changed: 56 additions & 1 deletion

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
// Regression test for https://github.com/facebook/yoga/issues/1665
9+
// flexBasis:0 + flexShrink:1 + borderWidth + minWidth produces an astronomically
10+
// large width (~1.65e11) instead of being clamped to minWidth.
11+
12+
#include <gtest/gtest.h>
13+
#include <yoga/Yoga.h>
14+
15+
TEST(YGFlexShrinkBorderBug, flex_basis_0_border_minwidth_row) {
16+
YGConfigRef config = YGConfigNew();
17+
YGNodeRef root = YGNodeNewWithConfig(config);
18+
YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow);
19+
YGNodeStyleSetWidth(root, 393.0f);
20+
YGNodeStyleSetHeight(root, 100.0f);
21+
22+
// Four row children: flexBasis:0, flexGrow:1, flexShrink:1, minWidth:160
23+
// First child has borderWidth:0.594443, rest 0.594442.
24+
// Bug: first child (and all others) get width ~1.65e11 instead of 160.
25+
float borders[] = {0.594443f, 0.594442f, 0.594442f, 0.594442f};
26+
for (int i = 0; i < 4; i++) {
27+
YGNodeRef child = YGNodeNewWithConfig(config);
28+
YGNodeStyleSetFlexBasis(child, 0.0f);
29+
YGNodeStyleSetFlexGrow(child, 1.0f);
30+
YGNodeStyleSetFlexShrink(child, 1.0f);
31+
YGNodeStyleSetMinWidth(child, 160.0f);
32+
YGNodeStyleSetBorder(child, YGEdgeAll, borders[i]);
33+
YGNodeInsertChild(root, child, i);
34+
}
35+
36+
YGNodeCalculateLayout(root, 393.0f, 100.0f, YGDirectionLTR);
37+
38+
for (int i = 0; i < 4; i++) {
39+
YGNodeRef child = YGNodeGetChild(root, i);
40+
EXPECT_GE(YGNodeLayoutGetWidth(child), 160.0f)
41+
<< "child[" << i << "] width below minWidth";
42+
EXPECT_LE(YGNodeLayoutGetWidth(child), 200.0f)
43+
<< "child[" << i << "] width is astronomically large (bug)";
44+
}
45+
46+
YGNodeFreeRecursive(root);
47+
YGConfigFree(config);
48+
}

yoga/algorithm/CalculateLayout.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,8 +925,15 @@ static float distributeFreeSpaceSecondPass(
925925
if (flexShrinkScaledFactor != 0) {
926926
float childSize = YGUndefined;
927927

928+
// Use a relative epsilon guard instead of exact equality: after the
929+
// first pass removes all constrained items, totalFlexShrinkScaledFactors
930+
// may be near-zero rather than exactly 0 due to floating-point
931+
// cancellation. Dividing by a near-zero value produces a gigantic
932+
// childSize that overwhelms the min/max clamp.
933+
const float shrinkFactorMagnitude =
934+
std::abs(flexLine.layout.totalFlexShrinkScaledFactors);
928935
if (yoga::isDefined(flexLine.layout.totalFlexShrinkScaledFactors) &&
929-
flexLine.layout.totalFlexShrinkScaledFactors == 0) {
936+
shrinkFactorMagnitude < 1e-6f) {
930937
childSize = childFlexBasis + flexShrinkScaledFactor;
931938
} else {
932939
childSize = childFlexBasis +

0 commit comments

Comments
 (0)