Skip to content

Commit ab6f07f

Browse files
javachemeta-codesync[bot]
authored andcommitted
Store immutable Fabric lookups in constexpr tables (#59516)
Summary: Pull Request resolved: #59516 Remove dynamic initialization and heap allocation from fixed Fabric command lookups and the debug layout-only prop list. Preserve exact matching and use length-indexed candidate groups to avoid scanning the full command tables. Changelog: [Internal] ___ landed-with-radar-review Differential Revision: D127582468 fbshipit-source-id: ba655996748750805b3a6cc59057ad4bd80a58c0
1 parent 1b27f4e commit ab6f07f

3 files changed

Lines changed: 281 additions & 49 deletions

File tree

‎packages/react-native/ReactAndroid/src/main/jni/react/fabric/AnimatedPropBufferEncoder.cpp‎

Lines changed: 85 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99

1010
#include <folly/Conv.h>
1111
#include <react/renderer/animationbackend/AnimatedPropsSerializer.h>
12+
#include <array>
1213
#include <numbers>
1314
#include <optional>
15+
#include <span>
1416
#include <string_view>
1517
#include <unordered_map>
18+
#include <utility>
1619

1720
namespace facebook::react {
1821

@@ -87,74 +90,110 @@ constexpr int CMD_TRANSFORM_PERSPECTIVE = 112;
8790
constexpr int CMD_UNIT_PX = 202;
8891
constexpr int CMD_UNIT_PERCENT = 203;
8992

93+
using CommandEntry = std::pair<std::string_view, int>;
94+
95+
// Group by name length so lookup only scans a small set of candidates.
96+
template <size_t MaxLength>
97+
constexpr auto indexCommandLengths(std::span<const CommandEntry> entries) {
98+
std::array<std::span<const CommandEntry>, MaxLength + 1> index{};
99+
size_t start = 0;
100+
for (size_t length = 0; length < index.size(); ++length) {
101+
auto end = start;
102+
while (end < entries.size() && entries[end].first.size() == length) {
103+
++end;
104+
}
105+
index[length] = entries.subspan(start, end - start);
106+
start = end;
107+
}
108+
return index;
109+
}
110+
111+
std::optional<int> findCommand(
112+
std::string_view name,
113+
std::span<const std::span<const CommandEntry>> index) {
114+
if (name.size() >= index.size()) {
115+
return std::nullopt;
116+
}
117+
for (const auto& [key, command] : index[name.size()]) {
118+
if (key == name) {
119+
return command;
120+
}
121+
}
122+
return std::nullopt;
123+
}
124+
90125
std::optional<int> propNameToCommand(const std::string& name) {
91-
static const std::unordered_map<std::string_view, int> kMap = {
126+
static constexpr std::array<CommandEntry, 37> kMap{{
127+
{"color", CMD_COLOR},
128+
{"zIndex", CMD_Z_INDEX},
92129
{"opacity", CMD_OPACITY},
93130
{"elevation", CMD_ELEVATION},
94-
{"zIndex", CMD_Z_INDEX},
95-
{"shadowOpacity", CMD_SHADOW_OPACITY},
96-
{"shadowRadius", CMD_SHADOW_RADIUS},
97-
{"backgroundColor", CMD_BACKGROUND_COLOR},
98-
{"color", CMD_COLOR},
99131
{"tintColor", CMD_TINT_COLOR},
100-
{"placeholderTextColor", CMD_PLACEHOLDER_TEXT_COLOR},
132+
{"transform", CMD_START_OF_TRANSFORM},
133+
{"borderColor", CMD_BORDER_COLOR},
101134
{"shadowColor", CMD_SHADOW_COLOR},
102135
{"borderRadius", CMD_BORDER_RADIUS},
136+
{"outlineColor", CMD_OUTLINE_COLOR},
137+
{"outlineWidth", CMD_OUTLINE_WIDTH},
138+
{"shadowRadius", CMD_SHADOW_RADIUS},
139+
{"outlineOffset", CMD_OUTLINE_OFFSET},
140+
{"shadowOpacity", CMD_SHADOW_OPACITY},
141+
{"borderEndColor", CMD_BORDER_END_COLOR},
142+
{"borderTopColor", CMD_BORDER_TOP_COLOR},
143+
{"backgroundColor", CMD_BACKGROUND_COLOR},
144+
{"borderLeftColor", CMD_BORDER_LEFT_COLOR},
145+
{"borderBlockColor", CMD_BORDER_BLOCK_COLOR},
146+
{"borderRightColor", CMD_BORDER_RIGHT_COLOR},
147+
{"borderStartColor", CMD_BORDER_START_COLOR},
148+
{"borderBottomColor", CMD_BORDER_BOTTOM_COLOR},
149+
{"borderEndEndRadius", CMD_BORDER_END_END_RADIUS},
150+
{"borderTopEndRadius", CMD_BORDER_TOP_END_RADIUS},
151+
{"borderBlockEndColor", CMD_BORDER_BLOCK_END_COLOR},
103152
{"borderTopLeftRadius", CMD_BORDER_TOP_LEFT_RADIUS},
153+
{"borderEndStartRadius", CMD_BORDER_END_START_RADIUS},
154+
{"borderStartEndRadius", CMD_BORDER_START_END_RADIUS},
104155
{"borderTopRightRadius", CMD_BORDER_TOP_RIGHT_RADIUS},
105156
{"borderTopStartRadius", CMD_BORDER_TOP_START_RADIUS},
106-
{"borderTopEndRadius", CMD_BORDER_TOP_END_RADIUS},
157+
{"placeholderTextColor", CMD_PLACEHOLDER_TEXT_COLOR},
158+
{"borderBlockStartColor", CMD_BORDER_BLOCK_START_COLOR},
159+
{"borderBottomEndRadius", CMD_BORDER_BOTTOM_END_RADIUS},
107160
{"borderBottomLeftRadius", CMD_BORDER_BOTTOM_LEFT_RADIUS},
161+
{"borderStartStartRadius", CMD_BORDER_START_START_RADIUS},
108162
{"borderBottomRightRadius", CMD_BORDER_BOTTOM_RIGHT_RADIUS},
109163
{"borderBottomStartRadius", CMD_BORDER_BOTTOM_START_RADIUS},
110-
{"borderBottomEndRadius", CMD_BORDER_BOTTOM_END_RADIUS},
111-
{"borderStartStartRadius", CMD_BORDER_START_START_RADIUS},
112-
{"borderStartEndRadius", CMD_BORDER_START_END_RADIUS},
113-
{"borderEndStartRadius", CMD_BORDER_END_START_RADIUS},
114-
{"borderEndEndRadius", CMD_BORDER_END_END_RADIUS},
115-
{"borderColor", CMD_BORDER_COLOR},
116-
{"borderTopColor", CMD_BORDER_TOP_COLOR},
117-
{"borderBottomColor", CMD_BORDER_BOTTOM_COLOR},
118-
{"borderLeftColor", CMD_BORDER_LEFT_COLOR},
119-
{"borderRightColor", CMD_BORDER_RIGHT_COLOR},
120-
{"borderStartColor", CMD_BORDER_START_COLOR},
121-
{"borderEndColor", CMD_BORDER_END_COLOR},
122-
{"borderBlockColor", CMD_BORDER_BLOCK_COLOR},
123-
{"borderBlockStartColor", CMD_BORDER_BLOCK_START_COLOR},
124-
{"borderBlockEndColor", CMD_BORDER_BLOCK_END_COLOR},
125-
{"outlineColor", CMD_OUTLINE_COLOR},
126-
{"outlineOffset", CMD_OUTLINE_OFFSET},
127-
{"outlineWidth", CMD_OUTLINE_WIDTH},
128-
{"transform", CMD_START_OF_TRANSFORM},
129-
};
130-
auto it = kMap.find(name);
131-
if (it == kMap.end()) {
132-
return std::nullopt;
133-
}
134-
return it->second;
164+
}};
165+
static constexpr auto kIndex =
166+
indexCommandLengths<kMap.back().first.size()>(kMap);
167+
static_assert(
168+
kIndex.back().data() ==
169+
std::span<const CommandEntry>{kMap}.last(kIndex.back().size()).data(),
170+
"Command entries must be sorted by name length");
171+
return findCommand(name, kIndex);
135172
}
136173

137174
std::optional<int> transformNameToCommand(const std::string& name) {
138-
static const std::unordered_map<std::string_view, int> kMap = {
139-
{"translateX", CMD_TRANSFORM_TRANSLATE_X},
140-
{"translateY", CMD_TRANSFORM_TRANSLATE_Y},
175+
static constexpr std::array<CommandEntry, 13> kMap{{
141176
{"scale", CMD_TRANSFORM_SCALE},
177+
{"skewX", CMD_TRANSFORM_SKEW_X},
178+
{"skewY", CMD_TRANSFORM_SKEW_Y},
179+
{"matrix", CMD_TRANSFORM_MATRIX},
180+
{"rotate", CMD_TRANSFORM_ROTATE},
142181
{"scaleX", CMD_TRANSFORM_SCALE_X},
143182
{"scaleY", CMD_TRANSFORM_SCALE_Y},
144-
{"rotate", CMD_TRANSFORM_ROTATE},
145183
{"rotateX", CMD_TRANSFORM_ROTATE_X},
146184
{"rotateY", CMD_TRANSFORM_ROTATE_Y},
147185
{"rotateZ", CMD_TRANSFORM_ROTATE_Z},
148-
{"skewX", CMD_TRANSFORM_SKEW_X},
149-
{"skewY", CMD_TRANSFORM_SKEW_Y},
150-
{"matrix", CMD_TRANSFORM_MATRIX},
186+
{"translateX", CMD_TRANSFORM_TRANSLATE_X},
187+
{"translateY", CMD_TRANSFORM_TRANSLATE_Y},
151188
{"perspective", CMD_TRANSFORM_PERSPECTIVE},
152-
};
153-
auto it = kMap.find(name);
154-
if (it == kMap.end()) {
155-
return std::nullopt;
156-
}
157-
return it->second;
189+
}};
190+
static constexpr auto kIndex =
191+
indexCommandLengths<kMap.back().first.size()>(kMap);
192+
static_assert(
193+
kIndex.back().data() ==
194+
std::span<const CommandEntry>{kMap}.last(kIndex.back().size()).data(),
195+
"Command entries must be sorted by name length");
196+
return findCommand(name, kIndex);
158197
}
159198

160199
bool packLength(

‎packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
#include <glog/logging.h>
2929

3030
#include <algorithm>
31+
#include <array>
3132
#include <cfenv>
3233
#include <cmath>
34+
#include <string_view>
3335
#include <vector>
3436

3537
namespace facebook::react {
@@ -81,7 +83,7 @@ namespace {
8183
// List of layout-only props extracted from ViewProps.kt used to filter out
8284
// component props from Props 1.5 to validate the Props 2.0 output
8385
inline bool isLayoutOnlyProp(const std::string& propName) {
84-
static const std::vector<std::string> layoutOnlyProps = {
86+
static constexpr std::array<std::string_view, 46> layoutOnlyProps = {
8587
// Flexbox Alignment
8688
"alignItems",
8789
"alignSelf",
@@ -146,8 +148,10 @@ inline bool isLayoutOnlyProp(const std::string& propName) {
146148
// Other
147149
"collapsable",
148150
};
149-
return std::find(layoutOnlyProps.begin(), layoutOnlyProps.end(), propName) !=
150-
layoutOnlyProps.end();
151+
return std::find(
152+
layoutOnlyProps.begin(),
153+
layoutOnlyProps.end(),
154+
std::string_view{propName}) != layoutOnlyProps.end();
151155
}
152156
#endif
153157

0 commit comments

Comments
 (0)