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
This commit is contained in:
Valentin Shergin
2019-03-18 23:38:12 -07:00
committed by Facebook Github Bot
parent 978e59aa92
commit be51d0564b
2 changed files with 62 additions and 1 deletions

View File

@@ -7,6 +7,7 @@
#include "ShadowNode.h"
#include <better/small_vector.h>
#include <string>
#include <react/core/ComponentDescriptor.h>
@@ -17,6 +18,8 @@
namespace facebook {
namespace react {
using AncestorList = ShadowNode::AncestorList;
SharedShadowNodeSharedList ShadowNode::emptySharedShadowNodeSharedList() {
static const auto emptySharedShadowNodeSharedList =
std::make_shared<SharedShadowNodeList>();
@@ -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<ShadowNodeFamily const *, 64>{};
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<std::reference_wrapper<const ShadowNode>> &ancestors) const {

View File

@@ -45,6 +45,11 @@ class ShadowNode : public virtual Sealable,
public:
using Shared = std::shared_ptr<const ShadowNode>;
using Weak = std::weak_ptr<const ShadowNode>;
using AncestorList = better::small_vector<
std::pair<
std::reference_wrapper<ShadowNode const> /* 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<std::reference_wrapper<const ShadowNode>> &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