mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 11:36:10 +00:00
LibWeb/DOM: Don't use recursion for subtree traversal in Node.h
This change is mainly motivated by the fact that iterating in a loop makes profiles easier to read and understand where time was spent in traversal callback. Additionally, using a loop reduces function call overhead and ensures constant stack usage.
This commit is contained in:
parent
dfcee2bbdf
commit
f5ba22d3e8
Notes:
github-actions[bot]
2025-02-03 17:38:04 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/f5ba22d3e8f Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3434 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/gmta ✅
1 changed files with 43 additions and 32 deletions
|
@ -15,6 +15,37 @@
|
|||
|
||||
namespace Web {
|
||||
|
||||
template<typename T, typename Callback>
|
||||
TraversalDecision traverse_preorder(T root, Callback callback)
|
||||
{
|
||||
T current = root;
|
||||
while (current != nullptr) {
|
||||
TraversalDecision decision = callback(*current);
|
||||
if (decision == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
|
||||
if (decision != TraversalDecision::SkipChildrenAndContinue && current->first_child() != nullptr) {
|
||||
current = current->first_child();
|
||||
continue;
|
||||
}
|
||||
if (current == root)
|
||||
break;
|
||||
|
||||
if (current->next_sibling() != nullptr) {
|
||||
current = current->next_sibling();
|
||||
continue;
|
||||
}
|
||||
|
||||
while (current != root && current->next_sibling() == nullptr)
|
||||
current = current->parent();
|
||||
if (current == root)
|
||||
break;
|
||||
|
||||
current = current->next_sibling();
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class TreeNode {
|
||||
public:
|
||||
|
@ -116,53 +147,33 @@ public:
|
|||
template<typename Callback>
|
||||
TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
|
||||
{
|
||||
if (auto decision = callback(static_cast<T const&>(*this)); decision != TraversalDecision::Continue)
|
||||
return decision;
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
return traverse_preorder(static_cast<T const*>(this), callback);
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
TraversalDecision for_each_in_inclusive_subtree(Callback callback)
|
||||
{
|
||||
if (auto decision = callback(static_cast<T&>(*this)); decision != TraversalDecision::Continue)
|
||||
return decision;
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
return traverse_preorder(static_cast<T*>(this), callback);
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback)
|
||||
{
|
||||
if (is<U>(static_cast<T const&>(*this))) {
|
||||
if (auto decision = callback(static_cast<U&>(*this)); decision != TraversalDecision::Continue)
|
||||
return decision;
|
||||
}
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
return for_each_in_inclusive_subtree([callback = move(callback)](T& node) {
|
||||
if (auto* maybe_node_of_type = as_if<U>(node))
|
||||
return callback(*maybe_node_of_type);
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename U, typename Callback>
|
||||
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
|
||||
{
|
||||
if (is<U>(static_cast<T const&>(*this))) {
|
||||
if (auto decision = callback(static_cast<U const&>(*this)); decision != TraversalDecision::Continue)
|
||||
return decision;
|
||||
}
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
return for_each_in_inclusive_subtree([callback = move(callback)](T const& node) {
|
||||
if (auto* maybe_node_of_type = as_if<U>(node))
|
||||
return callback(*maybe_node_of_type);
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
|
|
Loading…
Add table
Reference in a new issue