Coverage Report

Created: 2026-08-13 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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