From be51d0564b0df394e4f77017555751d587b306fc Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 18 Mar 2019 23:38:12 -0700 Subject: [PATCH] Fabric: a new efficient implementation of ShadowNode::getAncestors() Summary: The algorithm is quite simply: 1. Get family objects of the two nodes (inner and outer). 2. Traverse the list of family objects starting from the inner one and form a reversed list of them. 3. tarting from the inner node, traverse the tree layer-by-layer choosing a next element of the path by comparing the family object of each node on the level and an object from the list. Reviewed By: JoshuaGross Differential Revision: D14416950 fbshipit-source-id: 23c659a9e01690f90174193650a2b0ef09eadb4d --- .../fabric/core/shadownode/ShadowNode.cpp | 45 +++++++++++++++++++ .../fabric/core/shadownode/ShadowNode.h | 18 +++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.cpp b/ReactCommon/fabric/core/shadownode/ShadowNode.cpp index 15a16d33c..02c4302ce 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.cpp +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.cpp @@ -7,6 +7,7 @@ #include "ShadowNode.h" +#include #include #include @@ -17,6 +18,8 @@ namespace facebook { namespace react { +using AncestorList = ShadowNode::AncestorList; + SharedShadowNodeSharedList ShadowNode::emptySharedShadowNodeSharedList() { static const auto emptySharedShadowNodeSharedList = std::make_shared(); @@ -197,6 +200,48 @@ void ShadowNode::setMounted(bool mounted) const { } } +AncestorList ShadowNode::getAncestors( + ShadowNode const &ancestorShadowNode) const { + auto ancestors = AncestorList{}; + auto families = better::small_vector{}; + auto ancestorFamily = ancestorShadowNode.family_.get(); + auto descendantFamily = family_.get(); + + auto family = descendantFamily; + while (family && family != ancestorFamily) { + families.push_back(family); + family = family->parent_.lock().get(); + } + + if (family != ancestorFamily) { + ancestors.clear(); + return ancestors; + } + + auto parentNode = &ancestorShadowNode; + for (auto it = families.rbegin(); it != families.rend(); it++) { + auto childFamily = *it; + auto found = bool{false}; + auto childIndex = int{0}; + for (const auto &childNode : *parentNode->children_) { + if (childNode->family_.get() == childFamily) { + ancestors.push_back({*parentNode, childIndex}); + parentNode = childNode.get(); + found = true; + break; + } + childIndex++; + } + + if (!found) { + ancestors.clear(); + return ancestors; + } + } + + return ancestors; +} + bool ShadowNode::constructAncestorPath( const ShadowNode &ancestorShadowNode, std::vector> &ancestors) const { diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.h b/ReactCommon/fabric/core/shadownode/ShadowNode.h index 8e184322c..0a97a628a 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.h @@ -45,6 +45,11 @@ class ShadowNode : public virtual Sealable, public: using Shared = std::shared_ptr; using Weak = std::weak_ptr; + using AncestorList = better::small_vector< + std::pair< + std::reference_wrapper /* parentNode */, + int /* childIndex */>, + 64>; static SharedShadowNodeSharedList emptySharedShadowNodeSharedList(); @@ -138,6 +143,7 @@ class ShadowNode : public virtual Sealable, void setMounted(bool mounted) const; /* + * Deprecated. Use `getAncestors` instead. * Forms a list of all ancestors of the node relative to the given ancestor. * The list starts from the parent node and ends with the given ancestor node. * Returns `true` if successful, `false` otherwise. @@ -149,9 +155,19 @@ class ShadowNode : public virtual Sealable, * `childIndex` and `nodeId` tracking. */ bool constructAncestorPath( - const ShadowNode &rootShadowNode, + const ShadowNode &ancestorShadowNode, std::vector> &ancestors) const; + /* + * Returns a list of all ancestors of the node relative to the given ancestor. + * The list starts from the given ancestor node and ends with the parent node + * of `this` node. The elements of the list have a reference to some parent + * node and an index of the child of the parent node. + * Can be called from any thread. + * The theoretical complexity of the algorithm is `O(ln(n))`. Use it wisely. + */ + AncestorList getAncestors(ShadowNode const &ancestorShadowNode) const; + #pragma mark - DebugStringConvertible #if RN_DEBUG_STRING_CONVERTIBLE