123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 |
- /*
- * 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 "Differentiator.h"
- #include <better/map.h>
- #include <better/small_vector.h>
- #include <react/core/LayoutableShadowNode.h>
- #include <react/debug/SystraceSection.h>
- #include <algorithm>
- #include "ShadowView.h"
- namespace facebook {
- namespace react {
- /*
- * Extremely simple and naive implementation of a map.
- * The map is simple but it's optimized for particular constraints that we have
- * here.
- *
- * A regular map implementation (e.g. `std::unordered_map`) has some basic
- * performance guarantees like constant average insertion and lookup complexity.
- * This is nice, but it's *average* complexity measured on a non-trivial amount
- * of data. The regular map is a very complex data structure that using hashing,
- * buckets, multiple comprising operations, multiple allocations and so on.
- *
- * In our particular case, we need a map for `int` to `void *` with a dozen
- * values. In these conditions, nothing can beat a naive implementation using a
- * stack-allocated vector. And this implementation is exactly this: no
- * allocation, no hashing, no complex branching, no buckets, no iterators, no
- * rehashing, no other guarantees. It's crazy limited, unsafe, and performant on
- * a trivial amount of data.
- *
- * Besides that, we also need to optimize for insertion performance (the case
- * where a bunch of views appears on the screen first time); in this
- * implementation, this is as performant as vector `push_back`.
- */
- template <typename KeyT, typename ValueT, int DefaultSize = 16>
- class TinyMap final {
- public:
- using Pair = std::pair<KeyT, ValueT>;
- using Iterator = Pair *;
- /**
- * This must strictly only be called from outside of this class.
- */
- inline Iterator begin() {
- // Force a clean so that iterating over this TinyMap doesn't iterate over
- // erased elements. If all elements erased are at the front of the vector,
- // then we don't need to clean.
- cleanVector(erasedAtFront_ != numErased_);
- return begin_();
- }
- inline Iterator end() {
- // `back()` asserts on the vector being non-empty
- if (vector_.size() == 0 || numErased_ == vector_.size()) {
- return nullptr;
- }
- return &vector_.back() + 1;
- }
- inline Iterator find(KeyT key) {
- cleanVector();
- assert(key != 0);
- for (auto it = begin_() + erasedAtFront_; it != end(); it++) {
- if (it->first == key) {
- return it;
- }
- }
- return end();
- }
- inline void insert(Pair pair) {
- assert(pair.first != 0);
- vector_.push_back(pair);
- }
- inline void erase(Iterator iterator) {
- numErased_++;
- // Invalidate tag.
- iterator->first = 0;
- if (iterator == begin_() + erasedAtFront_) {
- erasedAtFront_++;
- }
- }
- private:
- /**
- * Same as begin() but doesn't call cleanVector at the beginning.
- */
- inline Iterator begin_() {
- // `front()` asserts on the vector being non-empty
- if (vector_.size() == 0 || vector_.size() == numErased_) {
- return nullptr;
- }
- return &vector_.front();
- }
- /**
- * Remove erased elements from internal vector.
- * We only modify the vector if erased elements are at least half of the
- * vector.
- */
- inline void cleanVector(bool forceClean = false) {
- if ((numErased_ < (vector_.size() / 2) && !forceClean) ||
- vector_.size() == 0 || numErased_ == 0 ||
- numErased_ == erasedAtFront_) {
- return;
- }
- if (numErased_ == vector_.size()) {
- vector_.clear();
- } else {
- vector_.erase(
- std::remove_if(
- vector_.begin(),
- vector_.end(),
- [](auto const &item) { return item.first == 0; }),
- vector_.end());
- }
- numErased_ = 0;
- erasedAtFront_ = 0;
- }
- better::small_vector<Pair, DefaultSize> vector_;
- int numErased_{0};
- int erasedAtFront_{0};
- };
- /*
- * 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::List &pairs) noexcept {
- if (pairs.size() < 2) {
- return;
- }
- auto isReorderNeeded = false;
- for (auto const &pair : pairs) {
- if (pair.shadowNode->getOrderIndex() != 0) {
- isReorderNeeded = true;
- break;
- }
- }
- if (!isReorderNeeded) {
- return;
- }
- std::stable_sort(
- pairs.begin(), pairs.end(), &shouldFirstPairComesBeforeSecondOne);
- }
- static void sliceChildShadowNodeViewPairsRecursively(
- ShadowViewNodePair::List &pairList,
- Point layoutOffset,
- ShadowNode const &shadowNode) {
- for (auto const &sharedChildShadowNode : shadowNode.getChildren()) {
- auto &childShadowNode = *sharedChildShadowNode;
- auto shadowView = ShadowView(childShadowNode);
- if (shadowView.layoutMetrics != EmptyLayoutMetrics) {
- shadowView.layoutMetrics.frame.origin += layoutOffset;
- }
- if (childShadowNode.getTraits().check(
- ShadowNodeTraits::Trait::FormsStackingContext)) {
- pairList.push_back({shadowView, &childShadowNode});
- } else {
- if (childShadowNode.getTraits().check(
- ShadowNodeTraits::Trait::FormsView)) {
- pairList.push_back({shadowView, &childShadowNode});
- }
- sliceChildShadowNodeViewPairsRecursively(
- pairList, shadowView.layoutMetrics.frame.origin, childShadowNode);
- }
- }
- }
- ShadowViewNodePair::List sliceChildShadowNodeViewPairs(
- ShadowNode const &shadowNode) {
- auto pairList = ShadowViewNodePair::List{};
- if (!shadowNode.getTraits().check(
- ShadowNodeTraits::Trait::FormsStackingContext) &&
- shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) {
- return pairList;
- }
- sliceChildShadowNodeViewPairsRecursively(pairList, {0, 0}, shadowNode);
- return pairList;
- }
- /*
- * Before we start to diff, let's make sure all our core data structures are in
- * good shape to deliver the best performance.
- */
- static_assert(
- std::is_move_constructible<ShadowViewMutation>::value,
- "`ShadowViewMutation` must be `move constructible`.");
- static_assert(
- std::is_move_constructible<ShadowView>::value,
- "`ShadowView` must be `move constructible`.");
- static_assert(
- std::is_move_constructible<ShadowViewNodePair>::value,
- "`ShadowViewNodePair` must be `move constructible`.");
- static_assert(
- std::is_move_constructible<ShadowViewNodePair::List>::value,
- "`ShadowViewNodePair::List` must be `move constructible`.");
- static_assert(
- std::is_move_assignable<ShadowViewMutation>::value,
- "`ShadowViewMutation` must be `move assignable`.");
- static_assert(
- std::is_move_assignable<ShadowView>::value,
- "`ShadowView` must be `move assignable`.");
- static_assert(
- std::is_move_assignable<ShadowViewNodePair>::value,
- "`ShadowViewNodePair` must be `move assignable`.");
- static_assert(
- std::is_move_assignable<ShadowViewNodePair::List>::value,
- "`ShadowViewNodePair::List` must be `move assignable`.");
- static void calculateShadowViewMutationsClassic(
- ShadowViewMutation::List &mutations,
- ShadowView const &parentShadowView,
- ShadowViewNodePair::List &&oldChildPairs,
- ShadowViewNodePair::List &&newChildPairs) {
- // This version of the algorithm is optimized for simplicity,
- // not for performance or optimal result.
- if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
- return;
- }
- // Sorting pairs based on `orderIndex` if needed.
- reorderInPlaceIfNeeded(oldChildPairs);
- reorderInPlaceIfNeeded(newChildPairs);
- auto index = int{0};
- // Maps inserted node tags to pointers to them in `newChildPairs`.
- auto insertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
- // Lists of mutations
- auto createMutations = ShadowViewMutation::List{};
- auto deleteMutations = ShadowViewMutation::List{};
- auto insertMutations = ShadowViewMutation::List{};
- auto removeMutations = ShadowViewMutation::List{};
- auto updateMutations = ShadowViewMutation::List{};
- auto downwardMutations = ShadowViewMutation::List{};
- auto destructiveDownwardMutations = ShadowViewMutation::List{};
- // Stage 1: Collecting `Update` mutations
- for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
- index++) {
- auto const &oldChildPair = oldChildPairs[index];
- auto const &newChildPair = newChildPairs[index];
- if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
- // Totally different nodes, updating is impossible.
- break;
- }
- if (oldChildPair.shadowView != newChildPair.shadowView) {
- updateMutations.push_back(ShadowViewMutation::UpdateMutation(
- parentShadowView,
- oldChildPair.shadowView,
- newChildPair.shadowView,
- index));
- }
- auto oldGrandChildPairs =
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
- auto newGrandChildPairs =
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
- calculateShadowViewMutationsClassic(
- *(newGrandChildPairs.size() ? &downwardMutations
- : &destructiveDownwardMutations),
- oldChildPair.shadowView,
- std::move(oldGrandChildPairs),
- std::move(newGrandChildPairs));
- }
- int lastIndexAfterFirstStage = index;
- // Stage 2: Collecting `Insert` mutations
- for (; index < newChildPairs.size(); index++) {
- auto const &newChildPair = newChildPairs[index];
- insertMutations.push_back(ShadowViewMutation::InsertMutation(
- parentShadowView, newChildPair.shadowView, index));
- insertedPairs.insert({newChildPair.shadowView.tag, &newChildPair});
- }
- // Stage 3: Collecting `Delete` and `Remove` mutations
- for (index = lastIndexAfterFirstStage; index < oldChildPairs.size();
- index++) {
- auto const &oldChildPair = oldChildPairs[index];
- // Even if the old view was (re)inserted, we have to generate `remove`
- // mutation.
- removeMutations.push_back(ShadowViewMutation::RemoveMutation(
- parentShadowView, oldChildPair.shadowView, index));
- auto const it = insertedPairs.find(oldChildPair.shadowView.tag);
- if (it == insertedPairs.end()) {
- // The old view was *not* (re)inserted.
- // We have to generate `delete` mutation and apply the algorithm
- // recursively.
- deleteMutations.push_back(
- ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
- // We also have to call the algorithm recursively to clean up the entire
- // subtree starting from the removed view.
- calculateShadowViewMutationsClassic(
- destructiveDownwardMutations,
- oldChildPair.shadowView,
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
- {});
- } else {
- // The old view *was* (re)inserted.
- // We have to call the algorithm recursively if the inserted view
- // is *not* the same as removed one.
- auto const &newChildPair = *it->second;
- if (newChildPair != oldChildPair) {
- auto oldGrandChildPairs =
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
- auto newGrandChildPairs =
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
- calculateShadowViewMutationsClassic(
- *(newGrandChildPairs.size() ? &downwardMutations
- : &destructiveDownwardMutations),
- newChildPair.shadowView,
- std::move(oldGrandChildPairs),
- std::move(newGrandChildPairs));
- }
- // In any case we have to remove the view from `insertedPairs` as
- // indication that the view was actually removed (which means that
- // the view existed before), hence we don't have to generate
- // `create` mutation.
- insertedPairs.erase(it);
- }
- }
- // Stage 4: Collecting `Create` mutations
- for (index = lastIndexAfterFirstStage; index < newChildPairs.size();
- index++) {
- auto const &newChildPair = newChildPairs[index];
- if (insertedPairs.find(newChildPair.shadowView.tag) ==
- insertedPairs.end()) {
- // The new view was (re)inserted, so there is no need to create it.
- continue;
- }
- createMutations.push_back(
- ShadowViewMutation::CreateMutation(newChildPair.shadowView));
- calculateShadowViewMutationsClassic(
- downwardMutations,
- newChildPair.shadowView,
- {},
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
- }
- // All mutations in an optimal order:
- std::move(
- destructiveDownwardMutations.begin(),
- destructiveDownwardMutations.end(),
- std::back_inserter(mutations));
- std::move(
- updateMutations.begin(),
- updateMutations.end(),
- std::back_inserter(mutations));
- std::move(
- removeMutations.rbegin(),
- removeMutations.rend(),
- std::back_inserter(mutations));
- std::move(
- deleteMutations.begin(),
- deleteMutations.end(),
- std::back_inserter(mutations));
- std::move(
- createMutations.begin(),
- createMutations.end(),
- std::back_inserter(mutations));
- std::move(
- downwardMutations.begin(),
- downwardMutations.end(),
- std::back_inserter(mutations));
- std::move(
- insertMutations.begin(),
- insertMutations.end(),
- std::back_inserter(mutations));
- }
- static void calculateShadowViewMutationsOptimizedMoves(
- ShadowViewMutation::List &mutations,
- ShadowView const &parentShadowView,
- ShadowViewNodePair::List &&oldChildPairs,
- ShadowViewNodePair::List &&newChildPairs) {
- if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
- return;
- }
- // Sorting pairs based on `orderIndex` if needed.
- reorderInPlaceIfNeeded(oldChildPairs);
- reorderInPlaceIfNeeded(newChildPairs);
- auto index = int{0};
- // Lists of mutations
- auto createMutations = ShadowViewMutation::List{};
- auto deleteMutations = ShadowViewMutation::List{};
- auto insertMutations = ShadowViewMutation::List{};
- auto removeMutations = ShadowViewMutation::List{};
- auto updateMutations = ShadowViewMutation::List{};
- auto downwardMutations = ShadowViewMutation::List{};
- auto destructiveDownwardMutations = ShadowViewMutation::List{};
- // Stage 1: Collecting `Update` mutations
- for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
- index++) {
- auto const &oldChildPair = oldChildPairs[index];
- auto const &newChildPair = newChildPairs[index];
- if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
- // Totally different nodes, updating is impossible.
- break;
- }
- if (oldChildPair.shadowView != newChildPair.shadowView) {
- updateMutations.push_back(ShadowViewMutation::UpdateMutation(
- parentShadowView,
- oldChildPair.shadowView,
- newChildPair.shadowView,
- index));
- }
- auto oldGrandChildPairs =
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
- auto newGrandChildPairs =
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
- calculateShadowViewMutationsOptimizedMoves(
- *(newGrandChildPairs.size() ? &downwardMutations
- : &destructiveDownwardMutations),
- oldChildPair.shadowView,
- std::move(oldGrandChildPairs),
- std::move(newGrandChildPairs));
- }
- int lastIndexAfterFirstStage = index;
- if (index == newChildPairs.size()) {
- // We've reached the end of the new children. We can delete+remove the
- // rest.
- for (; index < oldChildPairs.size(); index++) {
- auto const &oldChildPair = oldChildPairs[index];
- deleteMutations.push_back(
- ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
- removeMutations.push_back(ShadowViewMutation::RemoveMutation(
- parentShadowView, oldChildPair.shadowView, index));
- // We also have to call the algorithm recursively to clean up the entire
- // subtree starting from the removed view.
- calculateShadowViewMutationsOptimizedMoves(
- destructiveDownwardMutations,
- oldChildPair.shadowView,
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
- {});
- }
- } else if (index == oldChildPairs.size()) {
- // If we don't have any more existing children we can choose a fast path
- // since the rest will all be create+insert.
- for (; index < newChildPairs.size(); index++) {
- auto const &newChildPair = newChildPairs[index];
- insertMutations.push_back(ShadowViewMutation::InsertMutation(
- parentShadowView, newChildPair.shadowView, index));
- createMutations.push_back(
- ShadowViewMutation::CreateMutation(newChildPair.shadowView));
- calculateShadowViewMutationsOptimizedMoves(
- downwardMutations,
- newChildPair.shadowView,
- {},
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
- }
- } else {
- // Collect map of tags in the new list
- // In the future it would be nice to use TinyMap for newInsertedPairs, but
- // it's challenging to build an iterator that will work for our use-case
- // here.
- auto newRemainingPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
- auto newInsertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
- for (; index < newChildPairs.size(); index++) {
- auto const &newChildPair = newChildPairs[index];
- newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair});
- }
- // Walk through both lists at the same time
- // We will perform updates, create+insert, remove+delete, remove+insert
- // (move) here.
- int oldIndex = lastIndexAfterFirstStage,
- newIndex = lastIndexAfterFirstStage, newSize = newChildPairs.size(),
- oldSize = oldChildPairs.size();
- while (newIndex < newSize || oldIndex < oldSize) {
- bool haveNewPair = newIndex < newSize;
- bool haveOldPair = oldIndex < oldSize;
- // Advance both pointers if pointing to the same element
- if (haveNewPair && haveOldPair) {
- auto const &newChildPair = newChildPairs[newIndex];
- auto const &oldChildPair = oldChildPairs[oldIndex];
- int newTag = newChildPair.shadowView.tag;
- int oldTag = oldChildPair.shadowView.tag;
- if (newTag == oldTag) {
- // Generate Update instructions
- if (oldChildPair.shadowView != newChildPair.shadowView) {
- updateMutations.push_back(ShadowViewMutation::UpdateMutation(
- parentShadowView,
- oldChildPair.shadowView,
- newChildPair.shadowView,
- index));
- }
- // Remove from newRemainingPairs
- auto newRemainingPairIt = newRemainingPairs.find(oldTag);
- if (newRemainingPairIt != newRemainingPairs.end()) {
- newRemainingPairs.erase(newRemainingPairIt);
- }
- // Update subtrees
- auto oldGrandChildPairs =
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
- auto newGrandChildPairs =
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
- calculateShadowViewMutationsOptimizedMoves(
- *(newGrandChildPairs.size() ? &downwardMutations
- : &destructiveDownwardMutations),
- oldChildPair.shadowView,
- std::move(oldGrandChildPairs),
- std::move(newGrandChildPairs));
- newIndex++;
- oldIndex++;
- continue;
- }
- }
- if (haveOldPair) {
- auto const &oldChildPair = oldChildPairs[oldIndex];
- int oldTag = oldChildPair.shadowView.tag;
- // Was oldTag already inserted? This indicates a reordering, not just
- // a move. The new node has already been inserted, we just need to
- // remove the node from its old position now.
- auto const insertedIt = newInsertedPairs.find(oldTag);
- if (insertedIt != newInsertedPairs.end()) {
- removeMutations.push_back(ShadowViewMutation::RemoveMutation(
- parentShadowView, oldChildPair.shadowView, oldIndex));
- // Generate update instruction since we have an iterator ref to the
- // new node
- auto const &newChildPair = *insertedIt->second;
- if (oldChildPair.shadowView != newChildPair.shadowView) {
- updateMutations.push_back(ShadowViewMutation::UpdateMutation(
- parentShadowView,
- oldChildPair.shadowView,
- newChildPair.shadowView,
- index));
- }
- // Update subtrees
- auto oldGrandChildPairs =
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
- auto newGrandChildPairs =
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
- calculateShadowViewMutationsOptimizedMoves(
- *(newGrandChildPairs.size() ? &downwardMutations
- : &destructiveDownwardMutations),
- oldChildPair.shadowView,
- std::move(oldGrandChildPairs),
- std::move(newGrandChildPairs));
- newInsertedPairs.erase(insertedIt);
- oldIndex++;
- continue;
- }
- // Should we generate a delete+remove instruction for the old node?
- // If there's an old node and it's not found in the "new" list, we
- // generate remove+delete for this node and its subtree.
- auto const newIt = newRemainingPairs.find(oldTag);
- if (newIt == newRemainingPairs.end()) {
- removeMutations.push_back(ShadowViewMutation::RemoveMutation(
- parentShadowView, oldChildPair.shadowView, oldIndex));
- deleteMutations.push_back(
- ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
- // We also have to call the algorithm recursively to clean up the
- // entire subtree starting from the removed view.
- calculateShadowViewMutationsOptimizedMoves(
- destructiveDownwardMutations,
- oldChildPair.shadowView,
- sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
- {});
- oldIndex++;
- continue;
- }
- }
- // At this point, oldTag is -1 or is in the new list, and hasn't been
- // inserted or matched yet We're not sure yet if the new node is in the
- // old list - generate an insert instruction for the new node.
- auto const &newChildPair = newChildPairs[newIndex];
- insertMutations.push_back(ShadowViewMutation::InsertMutation(
- parentShadowView, newChildPair.shadowView, newIndex));
- newInsertedPairs.insert({newChildPair.shadowView.tag, &newChildPair});
- newIndex++;
- }
- // Final step: generate Create instructions for new nodes
- for (auto it = newInsertedPairs.begin(); it != newInsertedPairs.end();
- it++) {
- auto const &newChildPair = *it->second;
- createMutations.push_back(
- ShadowViewMutation::CreateMutation(newChildPair.shadowView));
- calculateShadowViewMutationsOptimizedMoves(
- downwardMutations,
- newChildPair.shadowView,
- {},
- sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
- }
- }
- // All mutations in an optimal order:
- std::move(
- destructiveDownwardMutations.begin(),
- destructiveDownwardMutations.end(),
- std::back_inserter(mutations));
- std::move(
- updateMutations.begin(),
- updateMutations.end(),
- std::back_inserter(mutations));
- std::move(
- removeMutations.rbegin(),
- removeMutations.rend(),
- std::back_inserter(mutations));
- std::move(
- deleteMutations.begin(),
- deleteMutations.end(),
- std::back_inserter(mutations));
- std::move(
- createMutations.begin(),
- createMutations.end(),
- std::back_inserter(mutations));
- std::move(
- downwardMutations.begin(),
- downwardMutations.end(),
- std::back_inserter(mutations));
- std::move(
- insertMutations.begin(),
- insertMutations.end(),
- std::back_inserter(mutations));
- }
- ShadowViewMutation::List calculateShadowViewMutations(
- DifferentiatorMode differentiatorMode,
- ShadowNode const &oldRootShadowNode,
- ShadowNode const &newRootShadowNode) {
- SystraceSection s("calculateShadowViewMutations");
- // Root shadow nodes must be belong the same family.
- assert(ShadowNode::sameFamily(oldRootShadowNode, newRootShadowNode));
- auto mutations = ShadowViewMutation::List{};
- mutations.reserve(256);
- auto oldRootShadowView = ShadowView(oldRootShadowNode);
- auto newRootShadowView = ShadowView(newRootShadowNode);
- if (oldRootShadowView != newRootShadowView) {
- mutations.push_back(ShadowViewMutation::UpdateMutation(
- ShadowView(), oldRootShadowView, newRootShadowView, -1));
- }
- if (differentiatorMode == DifferentiatorMode::Classic) {
- calculateShadowViewMutationsClassic(
- mutations,
- ShadowView(oldRootShadowNode),
- sliceChildShadowNodeViewPairs(oldRootShadowNode),
- sliceChildShadowNodeViewPairs(newRootShadowNode));
- } else {
- calculateShadowViewMutationsOptimizedMoves(
- mutations,
- ShadowView(oldRootShadowNode),
- sliceChildShadowNodeViewPairs(oldRootShadowNode),
- sliceChildShadowNodeViewPairs(newRootShadowNode));
- }
- return mutations;
- }
- } // namespace react
- } // namespace facebook
|