Differentiator.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. /*
  2. * Copyright (c) Facebook, Inc. and its 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. #include "Differentiator.h"
  8. #include <better/map.h>
  9. #include <better/small_vector.h>
  10. #include <react/core/LayoutableShadowNode.h>
  11. #include <react/debug/SystraceSection.h>
  12. #include <algorithm>
  13. #include "ShadowView.h"
  14. namespace facebook {
  15. namespace react {
  16. /*
  17. * Extremely simple and naive implementation of a map.
  18. * The map is simple but it's optimized for particular constraints that we have
  19. * here.
  20. *
  21. * A regular map implementation (e.g. `std::unordered_map`) has some basic
  22. * performance guarantees like constant average insertion and lookup complexity.
  23. * This is nice, but it's *average* complexity measured on a non-trivial amount
  24. * of data. The regular map is a very complex data structure that using hashing,
  25. * buckets, multiple comprising operations, multiple allocations and so on.
  26. *
  27. * In our particular case, we need a map for `int` to `void *` with a dozen
  28. * values. In these conditions, nothing can beat a naive implementation using a
  29. * stack-allocated vector. And this implementation is exactly this: no
  30. * allocation, no hashing, no complex branching, no buckets, no iterators, no
  31. * rehashing, no other guarantees. It's crazy limited, unsafe, and performant on
  32. * a trivial amount of data.
  33. *
  34. * Besides that, we also need to optimize for insertion performance (the case
  35. * where a bunch of views appears on the screen first time); in this
  36. * implementation, this is as performant as vector `push_back`.
  37. */
  38. template <typename KeyT, typename ValueT, int DefaultSize = 16>
  39. class TinyMap final {
  40. public:
  41. using Pair = std::pair<KeyT, ValueT>;
  42. using Iterator = Pair *;
  43. /**
  44. * This must strictly only be called from outside of this class.
  45. */
  46. inline Iterator begin() {
  47. // Force a clean so that iterating over this TinyMap doesn't iterate over
  48. // erased elements. If all elements erased are at the front of the vector,
  49. // then we don't need to clean.
  50. cleanVector(erasedAtFront_ != numErased_);
  51. return begin_();
  52. }
  53. inline Iterator end() {
  54. // `back()` asserts on the vector being non-empty
  55. if (vector_.size() == 0 || numErased_ == vector_.size()) {
  56. return nullptr;
  57. }
  58. return &vector_.back() + 1;
  59. }
  60. inline Iterator find(KeyT key) {
  61. cleanVector();
  62. assert(key != 0);
  63. for (auto it = begin_() + erasedAtFront_; it != end(); it++) {
  64. if (it->first == key) {
  65. return it;
  66. }
  67. }
  68. return end();
  69. }
  70. inline void insert(Pair pair) {
  71. assert(pair.first != 0);
  72. vector_.push_back(pair);
  73. }
  74. inline void erase(Iterator iterator) {
  75. numErased_++;
  76. // Invalidate tag.
  77. iterator->first = 0;
  78. if (iterator == begin_() + erasedAtFront_) {
  79. erasedAtFront_++;
  80. }
  81. }
  82. private:
  83. /**
  84. * Same as begin() but doesn't call cleanVector at the beginning.
  85. */
  86. inline Iterator begin_() {
  87. // `front()` asserts on the vector being non-empty
  88. if (vector_.size() == 0 || vector_.size() == numErased_) {
  89. return nullptr;
  90. }
  91. return &vector_.front();
  92. }
  93. /**
  94. * Remove erased elements from internal vector.
  95. * We only modify the vector if erased elements are at least half of the
  96. * vector.
  97. */
  98. inline void cleanVector(bool forceClean = false) {
  99. if ((numErased_ < (vector_.size() / 2) && !forceClean) ||
  100. vector_.size() == 0 || numErased_ == 0 ||
  101. numErased_ == erasedAtFront_) {
  102. return;
  103. }
  104. if (numErased_ == vector_.size()) {
  105. vector_.clear();
  106. } else {
  107. vector_.erase(
  108. std::remove_if(
  109. vector_.begin(),
  110. vector_.end(),
  111. [](auto const &item) { return item.first == 0; }),
  112. vector_.end());
  113. }
  114. numErased_ = 0;
  115. erasedAtFront_ = 0;
  116. }
  117. better::small_vector<Pair, DefaultSize> vector_;
  118. int numErased_{0};
  119. int erasedAtFront_{0};
  120. };
  121. /*
  122. * Sorting comparator for `reorderInPlaceIfNeeded`.
  123. */
  124. static bool shouldFirstPairComesBeforeSecondOne(
  125. ShadowViewNodePair const &lhs,
  126. ShadowViewNodePair const &rhs) noexcept {
  127. return lhs.shadowNode->getOrderIndex() < rhs.shadowNode->getOrderIndex();
  128. }
  129. /*
  130. * Reorders pairs in-place based on `orderIndex` using a stable sort algorithm.
  131. */
  132. static void reorderInPlaceIfNeeded(ShadowViewNodePair::List &pairs) noexcept {
  133. if (pairs.size() < 2) {
  134. return;
  135. }
  136. auto isReorderNeeded = false;
  137. for (auto const &pair : pairs) {
  138. if (pair.shadowNode->getOrderIndex() != 0) {
  139. isReorderNeeded = true;
  140. break;
  141. }
  142. }
  143. if (!isReorderNeeded) {
  144. return;
  145. }
  146. std::stable_sort(
  147. pairs.begin(), pairs.end(), &shouldFirstPairComesBeforeSecondOne);
  148. }
  149. static void sliceChildShadowNodeViewPairsRecursively(
  150. ShadowViewNodePair::List &pairList,
  151. Point layoutOffset,
  152. ShadowNode const &shadowNode) {
  153. for (auto const &sharedChildShadowNode : shadowNode.getChildren()) {
  154. auto &childShadowNode = *sharedChildShadowNode;
  155. auto shadowView = ShadowView(childShadowNode);
  156. if (shadowView.layoutMetrics != EmptyLayoutMetrics) {
  157. shadowView.layoutMetrics.frame.origin += layoutOffset;
  158. }
  159. if (childShadowNode.getTraits().check(
  160. ShadowNodeTraits::Trait::FormsStackingContext)) {
  161. pairList.push_back({shadowView, &childShadowNode});
  162. } else {
  163. if (childShadowNode.getTraits().check(
  164. ShadowNodeTraits::Trait::FormsView)) {
  165. pairList.push_back({shadowView, &childShadowNode});
  166. }
  167. sliceChildShadowNodeViewPairsRecursively(
  168. pairList, shadowView.layoutMetrics.frame.origin, childShadowNode);
  169. }
  170. }
  171. }
  172. ShadowViewNodePair::List sliceChildShadowNodeViewPairs(
  173. ShadowNode const &shadowNode) {
  174. auto pairList = ShadowViewNodePair::List{};
  175. if (!shadowNode.getTraits().check(
  176. ShadowNodeTraits::Trait::FormsStackingContext) &&
  177. shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) {
  178. return pairList;
  179. }
  180. sliceChildShadowNodeViewPairsRecursively(pairList, {0, 0}, shadowNode);
  181. return pairList;
  182. }
  183. /*
  184. * Before we start to diff, let's make sure all our core data structures are in
  185. * good shape to deliver the best performance.
  186. */
  187. static_assert(
  188. std::is_move_constructible<ShadowViewMutation>::value,
  189. "`ShadowViewMutation` must be `move constructible`.");
  190. static_assert(
  191. std::is_move_constructible<ShadowView>::value,
  192. "`ShadowView` must be `move constructible`.");
  193. static_assert(
  194. std::is_move_constructible<ShadowViewNodePair>::value,
  195. "`ShadowViewNodePair` must be `move constructible`.");
  196. static_assert(
  197. std::is_move_constructible<ShadowViewNodePair::List>::value,
  198. "`ShadowViewNodePair::List` must be `move constructible`.");
  199. static_assert(
  200. std::is_move_assignable<ShadowViewMutation>::value,
  201. "`ShadowViewMutation` must be `move assignable`.");
  202. static_assert(
  203. std::is_move_assignable<ShadowView>::value,
  204. "`ShadowView` must be `move assignable`.");
  205. static_assert(
  206. std::is_move_assignable<ShadowViewNodePair>::value,
  207. "`ShadowViewNodePair` must be `move assignable`.");
  208. static_assert(
  209. std::is_move_assignable<ShadowViewNodePair::List>::value,
  210. "`ShadowViewNodePair::List` must be `move assignable`.");
  211. static void calculateShadowViewMutationsClassic(
  212. ShadowViewMutation::List &mutations,
  213. ShadowView const &parentShadowView,
  214. ShadowViewNodePair::List &&oldChildPairs,
  215. ShadowViewNodePair::List &&newChildPairs) {
  216. // This version of the algorithm is optimized for simplicity,
  217. // not for performance or optimal result.
  218. if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
  219. return;
  220. }
  221. // Sorting pairs based on `orderIndex` if needed.
  222. reorderInPlaceIfNeeded(oldChildPairs);
  223. reorderInPlaceIfNeeded(newChildPairs);
  224. auto index = int{0};
  225. // Maps inserted node tags to pointers to them in `newChildPairs`.
  226. auto insertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
  227. // Lists of mutations
  228. auto createMutations = ShadowViewMutation::List{};
  229. auto deleteMutations = ShadowViewMutation::List{};
  230. auto insertMutations = ShadowViewMutation::List{};
  231. auto removeMutations = ShadowViewMutation::List{};
  232. auto updateMutations = ShadowViewMutation::List{};
  233. auto downwardMutations = ShadowViewMutation::List{};
  234. auto destructiveDownwardMutations = ShadowViewMutation::List{};
  235. // Stage 1: Collecting `Update` mutations
  236. for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
  237. index++) {
  238. auto const &oldChildPair = oldChildPairs[index];
  239. auto const &newChildPair = newChildPairs[index];
  240. if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
  241. // Totally different nodes, updating is impossible.
  242. break;
  243. }
  244. if (oldChildPair.shadowView != newChildPair.shadowView) {
  245. updateMutations.push_back(ShadowViewMutation::UpdateMutation(
  246. parentShadowView,
  247. oldChildPair.shadowView,
  248. newChildPair.shadowView,
  249. index));
  250. }
  251. auto oldGrandChildPairs =
  252. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
  253. auto newGrandChildPairs =
  254. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
  255. calculateShadowViewMutationsClassic(
  256. *(newGrandChildPairs.size() ? &downwardMutations
  257. : &destructiveDownwardMutations),
  258. oldChildPair.shadowView,
  259. std::move(oldGrandChildPairs),
  260. std::move(newGrandChildPairs));
  261. }
  262. int lastIndexAfterFirstStage = index;
  263. // Stage 2: Collecting `Insert` mutations
  264. for (; index < newChildPairs.size(); index++) {
  265. auto const &newChildPair = newChildPairs[index];
  266. insertMutations.push_back(ShadowViewMutation::InsertMutation(
  267. parentShadowView, newChildPair.shadowView, index));
  268. insertedPairs.insert({newChildPair.shadowView.tag, &newChildPair});
  269. }
  270. // Stage 3: Collecting `Delete` and `Remove` mutations
  271. for (index = lastIndexAfterFirstStage; index < oldChildPairs.size();
  272. index++) {
  273. auto const &oldChildPair = oldChildPairs[index];
  274. // Even if the old view was (re)inserted, we have to generate `remove`
  275. // mutation.
  276. removeMutations.push_back(ShadowViewMutation::RemoveMutation(
  277. parentShadowView, oldChildPair.shadowView, index));
  278. auto const it = insertedPairs.find(oldChildPair.shadowView.tag);
  279. if (it == insertedPairs.end()) {
  280. // The old view was *not* (re)inserted.
  281. // We have to generate `delete` mutation and apply the algorithm
  282. // recursively.
  283. deleteMutations.push_back(
  284. ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
  285. // We also have to call the algorithm recursively to clean up the entire
  286. // subtree starting from the removed view.
  287. calculateShadowViewMutationsClassic(
  288. destructiveDownwardMutations,
  289. oldChildPair.shadowView,
  290. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
  291. {});
  292. } else {
  293. // The old view *was* (re)inserted.
  294. // We have to call the algorithm recursively if the inserted view
  295. // is *not* the same as removed one.
  296. auto const &newChildPair = *it->second;
  297. if (newChildPair != oldChildPair) {
  298. auto oldGrandChildPairs =
  299. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
  300. auto newGrandChildPairs =
  301. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
  302. calculateShadowViewMutationsClassic(
  303. *(newGrandChildPairs.size() ? &downwardMutations
  304. : &destructiveDownwardMutations),
  305. newChildPair.shadowView,
  306. std::move(oldGrandChildPairs),
  307. std::move(newGrandChildPairs));
  308. }
  309. // In any case we have to remove the view from `insertedPairs` as
  310. // indication that the view was actually removed (which means that
  311. // the view existed before), hence we don't have to generate
  312. // `create` mutation.
  313. insertedPairs.erase(it);
  314. }
  315. }
  316. // Stage 4: Collecting `Create` mutations
  317. for (index = lastIndexAfterFirstStage; index < newChildPairs.size();
  318. index++) {
  319. auto const &newChildPair = newChildPairs[index];
  320. if (insertedPairs.find(newChildPair.shadowView.tag) ==
  321. insertedPairs.end()) {
  322. // The new view was (re)inserted, so there is no need to create it.
  323. continue;
  324. }
  325. createMutations.push_back(
  326. ShadowViewMutation::CreateMutation(newChildPair.shadowView));
  327. calculateShadowViewMutationsClassic(
  328. downwardMutations,
  329. newChildPair.shadowView,
  330. {},
  331. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
  332. }
  333. // All mutations in an optimal order:
  334. std::move(
  335. destructiveDownwardMutations.begin(),
  336. destructiveDownwardMutations.end(),
  337. std::back_inserter(mutations));
  338. std::move(
  339. updateMutations.begin(),
  340. updateMutations.end(),
  341. std::back_inserter(mutations));
  342. std::move(
  343. removeMutations.rbegin(),
  344. removeMutations.rend(),
  345. std::back_inserter(mutations));
  346. std::move(
  347. deleteMutations.begin(),
  348. deleteMutations.end(),
  349. std::back_inserter(mutations));
  350. std::move(
  351. createMutations.begin(),
  352. createMutations.end(),
  353. std::back_inserter(mutations));
  354. std::move(
  355. downwardMutations.begin(),
  356. downwardMutations.end(),
  357. std::back_inserter(mutations));
  358. std::move(
  359. insertMutations.begin(),
  360. insertMutations.end(),
  361. std::back_inserter(mutations));
  362. }
  363. static void calculateShadowViewMutationsOptimizedMoves(
  364. ShadowViewMutation::List &mutations,
  365. ShadowView const &parentShadowView,
  366. ShadowViewNodePair::List &&oldChildPairs,
  367. ShadowViewNodePair::List &&newChildPairs) {
  368. if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
  369. return;
  370. }
  371. // Sorting pairs based on `orderIndex` if needed.
  372. reorderInPlaceIfNeeded(oldChildPairs);
  373. reorderInPlaceIfNeeded(newChildPairs);
  374. auto index = int{0};
  375. // Lists of mutations
  376. auto createMutations = ShadowViewMutation::List{};
  377. auto deleteMutations = ShadowViewMutation::List{};
  378. auto insertMutations = ShadowViewMutation::List{};
  379. auto removeMutations = ShadowViewMutation::List{};
  380. auto updateMutations = ShadowViewMutation::List{};
  381. auto downwardMutations = ShadowViewMutation::List{};
  382. auto destructiveDownwardMutations = ShadowViewMutation::List{};
  383. // Stage 1: Collecting `Update` mutations
  384. for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
  385. index++) {
  386. auto const &oldChildPair = oldChildPairs[index];
  387. auto const &newChildPair = newChildPairs[index];
  388. if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
  389. // Totally different nodes, updating is impossible.
  390. break;
  391. }
  392. if (oldChildPair.shadowView != newChildPair.shadowView) {
  393. updateMutations.push_back(ShadowViewMutation::UpdateMutation(
  394. parentShadowView,
  395. oldChildPair.shadowView,
  396. newChildPair.shadowView,
  397. index));
  398. }
  399. auto oldGrandChildPairs =
  400. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
  401. auto newGrandChildPairs =
  402. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
  403. calculateShadowViewMutationsOptimizedMoves(
  404. *(newGrandChildPairs.size() ? &downwardMutations
  405. : &destructiveDownwardMutations),
  406. oldChildPair.shadowView,
  407. std::move(oldGrandChildPairs),
  408. std::move(newGrandChildPairs));
  409. }
  410. int lastIndexAfterFirstStage = index;
  411. if (index == newChildPairs.size()) {
  412. // We've reached the end of the new children. We can delete+remove the
  413. // rest.
  414. for (; index < oldChildPairs.size(); index++) {
  415. auto const &oldChildPair = oldChildPairs[index];
  416. deleteMutations.push_back(
  417. ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
  418. removeMutations.push_back(ShadowViewMutation::RemoveMutation(
  419. parentShadowView, oldChildPair.shadowView, index));
  420. // We also have to call the algorithm recursively to clean up the entire
  421. // subtree starting from the removed view.
  422. calculateShadowViewMutationsOptimizedMoves(
  423. destructiveDownwardMutations,
  424. oldChildPair.shadowView,
  425. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
  426. {});
  427. }
  428. } else if (index == oldChildPairs.size()) {
  429. // If we don't have any more existing children we can choose a fast path
  430. // since the rest will all be create+insert.
  431. for (; index < newChildPairs.size(); index++) {
  432. auto const &newChildPair = newChildPairs[index];
  433. insertMutations.push_back(ShadowViewMutation::InsertMutation(
  434. parentShadowView, newChildPair.shadowView, index));
  435. createMutations.push_back(
  436. ShadowViewMutation::CreateMutation(newChildPair.shadowView));
  437. calculateShadowViewMutationsOptimizedMoves(
  438. downwardMutations,
  439. newChildPair.shadowView,
  440. {},
  441. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
  442. }
  443. } else {
  444. // Collect map of tags in the new list
  445. // In the future it would be nice to use TinyMap for newInsertedPairs, but
  446. // it's challenging to build an iterator that will work for our use-case
  447. // here.
  448. auto newRemainingPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
  449. auto newInsertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
  450. for (; index < newChildPairs.size(); index++) {
  451. auto const &newChildPair = newChildPairs[index];
  452. newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair});
  453. }
  454. // Walk through both lists at the same time
  455. // We will perform updates, create+insert, remove+delete, remove+insert
  456. // (move) here.
  457. int oldIndex = lastIndexAfterFirstStage,
  458. newIndex = lastIndexAfterFirstStage, newSize = newChildPairs.size(),
  459. oldSize = oldChildPairs.size();
  460. while (newIndex < newSize || oldIndex < oldSize) {
  461. bool haveNewPair = newIndex < newSize;
  462. bool haveOldPair = oldIndex < oldSize;
  463. // Advance both pointers if pointing to the same element
  464. if (haveNewPair && haveOldPair) {
  465. auto const &newChildPair = newChildPairs[newIndex];
  466. auto const &oldChildPair = oldChildPairs[oldIndex];
  467. int newTag = newChildPair.shadowView.tag;
  468. int oldTag = oldChildPair.shadowView.tag;
  469. if (newTag == oldTag) {
  470. // Generate Update instructions
  471. if (oldChildPair.shadowView != newChildPair.shadowView) {
  472. updateMutations.push_back(ShadowViewMutation::UpdateMutation(
  473. parentShadowView,
  474. oldChildPair.shadowView,
  475. newChildPair.shadowView,
  476. index));
  477. }
  478. // Remove from newRemainingPairs
  479. auto newRemainingPairIt = newRemainingPairs.find(oldTag);
  480. if (newRemainingPairIt != newRemainingPairs.end()) {
  481. newRemainingPairs.erase(newRemainingPairIt);
  482. }
  483. // Update subtrees
  484. auto oldGrandChildPairs =
  485. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
  486. auto newGrandChildPairs =
  487. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
  488. calculateShadowViewMutationsOptimizedMoves(
  489. *(newGrandChildPairs.size() ? &downwardMutations
  490. : &destructiveDownwardMutations),
  491. oldChildPair.shadowView,
  492. std::move(oldGrandChildPairs),
  493. std::move(newGrandChildPairs));
  494. newIndex++;
  495. oldIndex++;
  496. continue;
  497. }
  498. }
  499. if (haveOldPair) {
  500. auto const &oldChildPair = oldChildPairs[oldIndex];
  501. int oldTag = oldChildPair.shadowView.tag;
  502. // Was oldTag already inserted? This indicates a reordering, not just
  503. // a move. The new node has already been inserted, we just need to
  504. // remove the node from its old position now.
  505. auto const insertedIt = newInsertedPairs.find(oldTag);
  506. if (insertedIt != newInsertedPairs.end()) {
  507. removeMutations.push_back(ShadowViewMutation::RemoveMutation(
  508. parentShadowView, oldChildPair.shadowView, oldIndex));
  509. // Generate update instruction since we have an iterator ref to the
  510. // new node
  511. auto const &newChildPair = *insertedIt->second;
  512. if (oldChildPair.shadowView != newChildPair.shadowView) {
  513. updateMutations.push_back(ShadowViewMutation::UpdateMutation(
  514. parentShadowView,
  515. oldChildPair.shadowView,
  516. newChildPair.shadowView,
  517. index));
  518. }
  519. // Update subtrees
  520. auto oldGrandChildPairs =
  521. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
  522. auto newGrandChildPairs =
  523. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
  524. calculateShadowViewMutationsOptimizedMoves(
  525. *(newGrandChildPairs.size() ? &downwardMutations
  526. : &destructiveDownwardMutations),
  527. oldChildPair.shadowView,
  528. std::move(oldGrandChildPairs),
  529. std::move(newGrandChildPairs));
  530. newInsertedPairs.erase(insertedIt);
  531. oldIndex++;
  532. continue;
  533. }
  534. // Should we generate a delete+remove instruction for the old node?
  535. // If there's an old node and it's not found in the "new" list, we
  536. // generate remove+delete for this node and its subtree.
  537. auto const newIt = newRemainingPairs.find(oldTag);
  538. if (newIt == newRemainingPairs.end()) {
  539. removeMutations.push_back(ShadowViewMutation::RemoveMutation(
  540. parentShadowView, oldChildPair.shadowView, oldIndex));
  541. deleteMutations.push_back(
  542. ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
  543. // We also have to call the algorithm recursively to clean up the
  544. // entire subtree starting from the removed view.
  545. calculateShadowViewMutationsOptimizedMoves(
  546. destructiveDownwardMutations,
  547. oldChildPair.shadowView,
  548. sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
  549. {});
  550. oldIndex++;
  551. continue;
  552. }
  553. }
  554. // At this point, oldTag is -1 or is in the new list, and hasn't been
  555. // inserted or matched yet We're not sure yet if the new node is in the
  556. // old list - generate an insert instruction for the new node.
  557. auto const &newChildPair = newChildPairs[newIndex];
  558. insertMutations.push_back(ShadowViewMutation::InsertMutation(
  559. parentShadowView, newChildPair.shadowView, newIndex));
  560. newInsertedPairs.insert({newChildPair.shadowView.tag, &newChildPair});
  561. newIndex++;
  562. }
  563. // Final step: generate Create instructions for new nodes
  564. for (auto it = newInsertedPairs.begin(); it != newInsertedPairs.end();
  565. it++) {
  566. auto const &newChildPair = *it->second;
  567. createMutations.push_back(
  568. ShadowViewMutation::CreateMutation(newChildPair.shadowView));
  569. calculateShadowViewMutationsOptimizedMoves(
  570. downwardMutations,
  571. newChildPair.shadowView,
  572. {},
  573. sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
  574. }
  575. }
  576. // All mutations in an optimal order:
  577. std::move(
  578. destructiveDownwardMutations.begin(),
  579. destructiveDownwardMutations.end(),
  580. std::back_inserter(mutations));
  581. std::move(
  582. updateMutations.begin(),
  583. updateMutations.end(),
  584. std::back_inserter(mutations));
  585. std::move(
  586. removeMutations.rbegin(),
  587. removeMutations.rend(),
  588. std::back_inserter(mutations));
  589. std::move(
  590. deleteMutations.begin(),
  591. deleteMutations.end(),
  592. std::back_inserter(mutations));
  593. std::move(
  594. createMutations.begin(),
  595. createMutations.end(),
  596. std::back_inserter(mutations));
  597. std::move(
  598. downwardMutations.begin(),
  599. downwardMutations.end(),
  600. std::back_inserter(mutations));
  601. std::move(
  602. insertMutations.begin(),
  603. insertMutations.end(),
  604. std::back_inserter(mutations));
  605. }
  606. ShadowViewMutation::List calculateShadowViewMutations(
  607. DifferentiatorMode differentiatorMode,
  608. ShadowNode const &oldRootShadowNode,
  609. ShadowNode const &newRootShadowNode) {
  610. SystraceSection s("calculateShadowViewMutations");
  611. // Root shadow nodes must be belong the same family.
  612. assert(ShadowNode::sameFamily(oldRootShadowNode, newRootShadowNode));
  613. auto mutations = ShadowViewMutation::List{};
  614. mutations.reserve(256);
  615. auto oldRootShadowView = ShadowView(oldRootShadowNode);
  616. auto newRootShadowView = ShadowView(newRootShadowNode);
  617. if (oldRootShadowView != newRootShadowView) {
  618. mutations.push_back(ShadowViewMutation::UpdateMutation(
  619. ShadowView(), oldRootShadowView, newRootShadowView, -1));
  620. }
  621. if (differentiatorMode == DifferentiatorMode::Classic) {
  622. calculateShadowViewMutationsClassic(
  623. mutations,
  624. ShadowView(oldRootShadowNode),
  625. sliceChildShadowNodeViewPairs(oldRootShadowNode),
  626. sliceChildShadowNodeViewPairs(newRootShadowNode));
  627. } else {
  628. calculateShadowViewMutationsOptimizedMoves(
  629. mutations,
  630. ShadowView(oldRootShadowNode),
  631. sliceChildShadowNodeViewPairs(oldRootShadowNode),
  632. sliceChildShadowNodeViewPairs(newRootShadowNode));
  633. }
  634. return mutations;
  635. }
  636. } // namespace react
  637. } // namespace facebook