/src/yoga/yoga/node/LayoutableChildren.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and 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 | | |
8 | | #pragma once |
9 | | |
10 | | #include <cstdint> |
11 | | #include <forward_list> |
12 | | #include <utility> |
13 | | |
14 | | #include <yoga/enums/Display.h> |
15 | | |
16 | | namespace facebook::yoga { |
17 | | |
18 | | class Node; |
19 | | |
20 | | template <typename T> |
21 | | class LayoutableChildren { |
22 | | public: |
23 | | struct Iterator { |
24 | | using iterator_category = std::input_iterator_tag; |
25 | | using difference_type = std::ptrdiff_t; |
26 | | using value_type = T*; |
27 | | using pointer = T*; |
28 | | using reference = T*; |
29 | | |
30 | 41.3M | Iterator() = default; |
31 | | |
32 | | Iterator(const T* node, size_t childIndex) |
33 | 15.8M | : node_(node), childIndex_(childIndex) {} |
34 | | |
35 | 44.9M | T* operator*() const { |
36 | 44.9M | return node_->getChild(childIndex_); |
37 | 44.9M | } |
38 | | |
39 | 44.9M | Iterator& operator++() { |
40 | 44.9M | next(); |
41 | 44.9M | return *this; |
42 | 44.9M | } |
43 | | |
44 | 13.5M | Iterator operator++(int) { |
45 | 13.5M | Iterator tmp = *this; |
46 | 13.5M | ++(*this); |
47 | 13.5M | return tmp; |
48 | 13.5M | } |
49 | | |
50 | | friend bool operator==(const Iterator& a, const Iterator& b) { |
51 | | return a.node_ == b.node_ && a.childIndex_ == b.childIndex_; |
52 | | } |
53 | | |
54 | 70.5M | friend bool operator!=(const Iterator& a, const Iterator& b) { |
55 | 70.5M | return a.node_ != b.node_ || a.childIndex_ != b.childIndex_; |
56 | 70.5M | } |
57 | | |
58 | | private: |
59 | 44.9M | void next() { |
60 | 44.9M | if (childIndex_ + 1 >= node_->getChildCount()) { |
61 | | // if the current node has no more children, try to backtrack and |
62 | | // visit its successor |
63 | 15.8M | if (backtrack_.empty()) [[likely]] { |
64 | | // if there are no nodes to backtrack to, the last node has been |
65 | | // visited |
66 | 15.8M | *this = Iterator{}; |
67 | 15.8M | } else { |
68 | | // pop and restore the latest backtrack entry |
69 | 0 | const auto& back = backtrack_.front(); |
70 | 0 | node_ = back.first; |
71 | 0 | childIndex_ = back.second; |
72 | 0 | backtrack_.pop_front(); |
73 | | |
74 | | // go to the next node |
75 | 0 | next(); |
76 | 0 | } |
77 | 29.1M | } else { |
78 | | // current node has more children to visit, go to next |
79 | 29.1M | ++childIndex_; |
80 | | // skip all display: contents nodes, possibly going deeper into the |
81 | | // tree |
82 | 29.1M | if (node_->getChild(childIndex_)->style().display() == |
83 | 29.1M | Display::Contents) [[unlikely]] { |
84 | 0 | skipContentsNodes(); |
85 | 0 | } |
86 | 29.1M | } |
87 | 44.9M | } |
88 | | |
89 | 0 | void skipContentsNodes() { |
90 | | // get the node that would be returned from the iterator |
91 | 0 | auto currentNode = node_->getChild(childIndex_); |
92 | 0 | while (currentNode->style().display() == Display::Contents && |
93 | 0 | currentNode->getChildCount() > 0) { |
94 | | // if it has display: contents set, it shouldn't be returned but its |
95 | | // children should in its place push the current node and child index |
96 | | // so that the current state can be restored when backtracking |
97 | 0 | backtrack_.push_front({node_, childIndex_}); |
98 | | // traverse the child |
99 | 0 | node_ = currentNode; |
100 | 0 | childIndex_ = 0; |
101 | | |
102 | | // repeat until a node without display: contents is found in the |
103 | | // subtree or a leaf is reached |
104 | 0 | currentNode = currentNode->getChild(childIndex_); |
105 | 0 | } |
106 | | |
107 | | // if no node without display: contents was found, try to backtrack |
108 | 0 | if (currentNode->style().display() == Display::Contents) { |
109 | 0 | next(); |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | | const T* node_{nullptr}; |
114 | | size_t childIndex_{0}; |
115 | | std::forward_list<std::pair<const T*, size_t>> backtrack_; |
116 | | |
117 | | friend LayoutableChildren; |
118 | | }; |
119 | | |
120 | 29.5M | explicit LayoutableChildren(const T* node) : node_(node) { |
121 | 29.5M | static_assert(std::input_iterator<LayoutableChildren<T>::Iterator>); |
122 | 29.5M | static_assert( |
123 | 29.5M | std::is_base_of<Node, T>::value, |
124 | 29.5M | "Type parameter of LayoutableChildren must derive from yoga::Node"); |
125 | 29.5M | } |
126 | | |
127 | 15.8M | Iterator begin() const { |
128 | 15.8M | if (node_->getChildCount() > 0) { |
129 | 15.8M | auto result = Iterator(node_, 0); |
130 | 15.8M | if (node_->getChild(0)->style().display() == Display::Contents) |
131 | 0 | [[unlikely]] { |
132 | 0 | result.skipContentsNodes(); |
133 | 0 | } |
134 | 15.8M | return result; |
135 | 15.8M | } else { |
136 | 0 | return Iterator{}; |
137 | 0 | } |
138 | 15.8M | } |
139 | | |
140 | 25.5M | Iterator end() const { |
141 | 25.5M | return Iterator{}; |
142 | 25.5M | } |
143 | | |
144 | | private: |
145 | | const T* node_; |
146 | | }; |
147 | | |
148 | | } // namespace facebook::yoga |