1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
/*
* Copyright (C) 2016 The Qt Company Ltd.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cmath>
#include <cstdint>
#include <limits>
#include <yoga/YGMacros.h>
#include <yoga/YGValue.h>
#if defined(__has_include) && __has_include(<version>)
// needed to be able to evaluate defined(__cpp_lib_bit_cast)
#include <version>
#else
// needed to be able to evaluate defined(__cpp_lib_bit_cast)
#include <ciso646>
#endif
#ifdef __cpp_lib_bit_cast
#include <bit>
#else
#include <cstring>
#endif
static_assert(
std::numeric_limits<float>::is_iec559,
"facebook::yoga::detail::CompactValue only works with IEEE754 floats");
#ifdef YOGA_COMPACT_VALUE_TEST
#define VISIBLE_FOR_TESTING public:
#else
#define VISIBLE_FOR_TESTING private:
#endif
namespace facebook {
namespace yoga {
namespace detail {
// This class stores YGValue in 32 bits.
// - The value does not matter for Undefined and Auto. NaNs are used for their
// representation.
// - To differentiate between Point and Percent, one exponent bit is used.
// Supported the range [0x40, 0xbf] (0xbf is inclusive for point, but
// exclusive for percent).
// - Value ranges:
// points: 1.08420217e-19f to 36893485948395847680
// 0x00000000 0x3fffffff
// percent: 1.08420217e-19f to 18446742974197923840
// 0x40000000 0x7f7fffff
// - Zero is supported, negative zero is not
// - values outside of the representable range are clamped
class YOGA_EXPORT CompactValue {
friend constexpr bool operator==(CompactValue, CompactValue) noexcept;
public:
static constexpr auto LOWER_BOUND = 1.08420217e-19f;
static constexpr auto UPPER_BOUND_POINT = 36893485948395847680.0f;
static constexpr auto UPPER_BOUND_PERCENT = 18446742974197923840.0f;
template <YGUnit Unit>
static CompactValue of(float value) noexcept {
if (value == 0.0f || (value < LOWER_BOUND && value > -LOWER_BOUND)) {
constexpr auto zero =
Unit == YGUnitPercent ? ZERO_BITS_PERCENT : ZERO_BITS_POINT;
return {zero};
}
constexpr auto upperBound =
Unit == YGUnitPercent ? UPPER_BOUND_PERCENT : UPPER_BOUND_POINT;
if (value > upperBound || value < -upperBound) {
value = copysignf(upperBound, value);
}
uint32_t unitBit = Unit == YGUnitPercent ? PERCENT_BIT : 0;
auto data = asU32(value);
data -= BIAS;
data |= unitBit;
return {data};
}
template <YGUnit Unit>
static CompactValue ofMaybe(float value) noexcept {
return std::isnan(value) || std::isinf(value) ? ofUndefined()
: of<Unit>(value);
}
static constexpr CompactValue ofZero() noexcept {
return CompactValue{ZERO_BITS_POINT};
}
static constexpr CompactValue ofUndefined() noexcept {
return CompactValue{};
}
static constexpr CompactValue ofAuto() noexcept {
return CompactValue{AUTO_BITS};
}
constexpr CompactValue() noexcept : repr_(0x7FC00000) {}
CompactValue(const YGValue& x) noexcept : repr_(uint32_t{0}) {
switch (x.unit) {
case YGUnitUndefined:
*this = ofUndefined();
break;
case YGUnitAuto:
*this = ofAuto();
break;
case YGUnitPoint:
*this = of<YGUnitPoint>(x.value);
break;
case YGUnitPercent:
*this = of<YGUnitPercent>(x.value);
break;
}
}
operator YGValue() const noexcept {
switch (repr_) {
case AUTO_BITS:
return YGValueAuto;
case ZERO_BITS_POINT:
return YGValue{0.0f, YGUnitPoint};
case ZERO_BITS_PERCENT:
return YGValue{0.0f, YGUnitPercent};
}
if (std::isnan(asFloat(repr_))) {
return YGValueUndefined;
}
auto data = repr_;
data &= ~PERCENT_BIT;
data += BIAS;
return YGValue{
asFloat(data), repr_ & 0x40000000 ? YGUnitPercent : YGUnitPoint};
}
bool isUndefined() const noexcept {
return (
repr_ != AUTO_BITS && repr_ != ZERO_BITS_POINT &&
repr_ != ZERO_BITS_PERCENT && std::isnan(asFloat(repr_)));
}
bool isAuto() const noexcept { return repr_ == AUTO_BITS; }
private:
uint32_t repr_;
static constexpr uint32_t BIAS = 0x20000000;
static constexpr uint32_t PERCENT_BIT = 0x40000000;
// these are signaling NaNs with specific bit pattern as payload they will be
// silenced whenever going through an FPU operation on ARM + x86
static constexpr uint32_t AUTO_BITS = 0x7faaaaaa;
static constexpr uint32_t ZERO_BITS_POINT = 0x7f8f0f0f;
static constexpr uint32_t ZERO_BITS_PERCENT = 0x7f80f0f0;
constexpr CompactValue(uint32_t data) noexcept : repr_(data) {}
VISIBLE_FOR_TESTING uint32_t repr() { return repr_; }
static uint32_t asU32(float f) {
#ifdef __cpp_lib_bit_cast
return std::bit_cast<uint32_t>(f);
#else
uint32_t u;
static_assert(
sizeof(u) == sizeof(f), "uint32_t and float must have the same size");
std::memcpy(&u, &f, sizeof(f));
return u;
#endif
}
static float asFloat(uint32_t u) {
#ifdef __cpp_lib_bit_cast
return std::bit_cast<float>(u);
#else
float f;
static_assert(
sizeof(f) == sizeof(u), "uint32_t and float must have the same size");
std::memcpy(&f, &u, sizeof(u));
return f;
#endif
}
};
template <>
CompactValue CompactValue::of<YGUnitUndefined>(float) noexcept = delete;
template <>
CompactValue CompactValue::of<YGUnitAuto>(float) noexcept = delete;
template <>
CompactValue CompactValue::ofMaybe<YGUnitUndefined>(float) noexcept = delete;
template <>
CompactValue CompactValue::ofMaybe<YGUnitAuto>(float) noexcept = delete;
constexpr bool operator==(CompactValue a, CompactValue b) noexcept {
return a.repr_ == b.repr_;
}
constexpr bool operator!=(CompactValue a, CompactValue b) noexcept {
return !(a == b);
}
} // namespace detail
} // namespace yoga
} // namespace facebook
|