mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
c22b874fd6
Summary: Previously, `ShadowViewNodePair::List` owned each `ShadowViewNodePair` but whenever we put `ShadowViewNodePair` into a TinyMap, those were unowned pointer references. This worked... 99% of the time. But in some marginal cases, it would cause dangling pointers, leading to difficult-to-track-down issues. So, I'm moving both of these to be unowned pointers and keeping a `std::deque` that owns all `ShadowViewNodePair`s and is itself owned by the main differ function. See comments for more implementation details. I'm moderately concerned about memory usage regressions, but practically speaking this will contain many items when a tree is created for the first time, and then very few items after that (space complexity should be similar to `O(n)` where `n` is the number of changed nodes after the last diff). See comments as to why I believe `std::deque` is the right choice. Long-term there might be data-structures that are even more optimal, but std::deque has the right tradeoffs compared to other built-in STL structures like std::list and std::vector, and is probably better than std::forward_list too. Long-term we may want a custom data-structure that fits our needs exactly, but std::deque comes close and is possibly optimal. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D27730952 fbshipit-source-id: 2194b535439bd309803a221188da5db75242005a
101 lines
3.2 KiB
C++
101 lines
3.2 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "stubs.h"
|
|
|
|
#include <react/renderer/core/LayoutableShadowNode.h>
|
|
#include <react/renderer/core/ShadowNodeFragment.h>
|
|
#include <react/renderer/mounting/Differentiator.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
/*
|
|
* Sorting comparator for `reorderInPlaceIfNeeded`.
|
|
*/
|
|
static bool shouldFirstPairComesBeforeSecondOne(
|
|
ShadowViewNodePair const &lhs,
|
|
ShadowViewNodePair const &rhs) noexcept {
|
|
return lhs.shadowNode->getOrderIndex() < rhs.shadowNode->getOrderIndex();
|
|
}
|
|
|
|
/*
|
|
* Reorders pairs in-place based on `orderIndex` using a stable sort algorithm.
|
|
*/
|
|
static void reorderInPlaceIfNeeded(
|
|
ShadowViewNodePair::OwningList &pairs) noexcept {
|
|
// This is a simplified version of the function intentionally copied from
|
|
// `Differentiator.cpp`.
|
|
std::stable_sort(
|
|
pairs.begin(), pairs.end(), &shouldFirstPairComesBeforeSecondOne);
|
|
}
|
|
|
|
/*
|
|
* Generates `create` and `insert` instructions recursively traversing a shadow
|
|
* tree.
|
|
* This is a trivial implementation of diffing algorithm that can only "diff"
|
|
* an empty tree with some other one.
|
|
*/
|
|
static void calculateShadowViewMutationsForNewTree(
|
|
ShadowViewMutation::List &mutations,
|
|
ShadowView const &parentShadowView,
|
|
ShadowViewNodePair::OwningList newChildPairs) {
|
|
// Sorting pairs based on `orderIndex` if needed.
|
|
reorderInPlaceIfNeeded(newChildPairs);
|
|
|
|
for (auto index = 0; index < newChildPairs.size(); index++) {
|
|
auto const &newChildPair = newChildPairs[index];
|
|
|
|
mutations.push_back(
|
|
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
|
|
mutations.push_back(ShadowViewMutation::InsertMutation(
|
|
parentShadowView, newChildPair.shadowView, index));
|
|
|
|
auto newGrandChildPairs =
|
|
sliceChildShadowNodeViewPairsLegacy(*newChildPair.shadowNode);
|
|
|
|
calculateShadowViewMutationsForNewTree(
|
|
mutations, newChildPair.shadowView, newGrandChildPairs);
|
|
}
|
|
}
|
|
|
|
StubViewTree buildStubViewTreeWithoutUsingDifferentiator(
|
|
ShadowNode const &rootShadowNode) {
|
|
auto mutations = ShadowViewMutation::List{};
|
|
mutations.reserve(256);
|
|
|
|
calculateShadowViewMutationsForNewTree(
|
|
mutations,
|
|
ShadowView(rootShadowNode),
|
|
sliceChildShadowNodeViewPairsLegacy(rootShadowNode));
|
|
|
|
auto emptyRootShadowNode = rootShadowNode.clone(ShadowNodeFragment{
|
|
ShadowNodeFragment::propsPlaceholder(),
|
|
ShadowNode::emptySharedShadowNodeSharedList()});
|
|
|
|
auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode));
|
|
stubViewTree.mutate(mutations);
|
|
return stubViewTree;
|
|
}
|
|
|
|
StubViewTree buildStubViewTreeUsingDifferentiator(
|
|
ShadowNode const &rootShadowNode) {
|
|
auto emptyRootShadowNode = rootShadowNode.clone(ShadowNodeFragment{
|
|
ShadowNodeFragment::propsPlaceholder(),
|
|
ShadowNode::emptySharedShadowNodeSharedList()});
|
|
|
|
auto mutations =
|
|
calculateShadowViewMutations(*emptyRootShadowNode, rootShadowNode, true);
|
|
|
|
auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode));
|
|
stubViewTree.mutate(mutations);
|
|
return stubViewTree;
|
|
}
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|