Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmComputeComponentGraph.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmComputeComponentGraph.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <cstddef>
8
#include <limits>
9
10
size_t const cmComputeComponentGraph::INVALID_COMPONENT =
11
  std::numeric_limits<size_t>::max();
12
13
cmComputeComponentGraph::cmComputeComponentGraph(Graph const& input)
14
0
  : InputGraph(input)
15
0
{
16
0
}
17
18
0
cmComputeComponentGraph::~cmComputeComponentGraph() = default;
19
20
void cmComputeComponentGraph::Compute()
21
0
{
22
  // Identify components.
23
0
  this->Tarjan();
24
25
  // Compute the component graph.
26
0
  this->ComponentGraph.resize(0);
27
0
  this->ComponentGraph.resize(this->Components.size());
28
0
  this->TransferEdges();
29
0
}
30
31
void cmComputeComponentGraph::Tarjan()
32
0
{
33
0
  size_t n = this->InputGraph.size();
34
0
  TarjanEntry entry = { 0, 0 };
35
0
  this->TarjanEntries.resize(0);
36
0
  this->TarjanEntries.resize(n, entry);
37
0
  this->TarjanComponents.resize(0);
38
0
  this->TarjanComponents.resize(n, INVALID_COMPONENT);
39
0
  this->TarjanWalkId = 0;
40
0
  this->TarjanVisited.resize(0);
41
0
  this->TarjanVisited.resize(n, 0);
42
0
  for (size_t i = 0; i < n; ++i) {
43
    // Start a new DFS from this node if it has never been visited.
44
0
    if (!this->TarjanVisited[i]) {
45
0
      assert(this->TarjanStack.empty());
46
0
      ++this->TarjanWalkId;
47
0
      this->TarjanIndex = 0;
48
0
      this->TarjanVisit(i);
49
0
    }
50
0
  }
51
0
}
52
53
void cmComputeComponentGraph::TarjanVisit(size_t i)
54
0
{
55
  // We are now visiting this node.
56
0
  this->TarjanVisited[i] = this->TarjanWalkId;
57
58
  // Initialize the entry.
59
0
  this->TarjanEntries[i].Root = i;
60
0
  this->TarjanComponents[i] = INVALID_COMPONENT;
61
0
  this->TarjanEntries[i].VisitIndex = ++this->TarjanIndex;
62
0
  this->TarjanStack.push(i);
63
64
  // Follow outgoing edges.
65
0
  EdgeList const& nl = this->InputGraph[i];
66
0
  for (cmGraphEdge const& ni : nl) {
67
0
    size_t j = ni;
68
69
    // Ignore edges to nodes that have been reached by a previous DFS
70
    // walk.  Since we did not reach the current node from that walk
71
    // it must not belong to the same component and it has already
72
    // been assigned to a component.
73
0
    if (this->TarjanVisited[j] > 0 &&
74
0
        this->TarjanVisited[j] < this->TarjanWalkId) {
75
0
      continue;
76
0
    }
77
78
    // Visit the destination if it has not yet been visited.
79
0
    if (!this->TarjanVisited[j]) {
80
0
      this->TarjanVisit(j);
81
0
    }
82
83
    // If the destination has not yet been assigned to a component,
84
    // check if it has a better root for the current object.
85
0
    if (this->TarjanComponents[j] == INVALID_COMPONENT) {
86
0
      if (this->TarjanEntries[this->TarjanEntries[j].Root].VisitIndex <
87
0
          this->TarjanEntries[this->TarjanEntries[i].Root].VisitIndex) {
88
0
        this->TarjanEntries[i].Root = this->TarjanEntries[j].Root;
89
0
      }
90
0
    }
91
0
  }
92
93
  // Check if we have found a component.
94
0
  if (this->TarjanEntries[i].Root == i) {
95
    // Yes.  Create it.
96
0
    size_t c = this->Components.size();
97
0
    this->Components.emplace_back();
98
0
    NodeList& component = this->Components[c];
99
100
    // Populate the component list.
101
0
    size_t j;
102
0
    do {
103
      // Get the next member of the component.
104
0
      j = this->TarjanStack.top();
105
0
      this->TarjanStack.pop();
106
107
      // Assign the member to the component.
108
0
      this->TarjanComponents[j] = c;
109
0
      this->TarjanEntries[j].Root = i;
110
111
      // Store the node in its component.
112
0
      component.push_back(j);
113
0
    } while (j != i);
114
115
    // Sort the component members for clarity.
116
0
    std::sort(component.begin(), component.end());
117
0
  }
118
0
}
119
120
void cmComputeComponentGraph::TransferEdges()
121
0
{
122
  // Map inter-component edges in the original graph to edges in the
123
  // component graph.
124
0
  size_t n = this->InputGraph.size();
125
0
  for (size_t i = 0; i < n; ++i) {
126
0
    size_t i_component = this->TarjanComponents[i];
127
0
    EdgeList const& nl = this->InputGraph[i];
128
0
    for (cmGraphEdge const& ni : nl) {
129
0
      size_t j = ni;
130
0
      size_t j_component = this->TarjanComponents[j];
131
0
      if (i_component != j_component) {
132
        // We do not attempt to combine duplicate edges, but instead
133
        // store the inter-component edges with suitable multiplicity.
134
0
        this->ComponentGraph[i_component].emplace_back(
135
0
          j_component, ni.IsStrong(), ni.IsCross(), ni.GetBacktrace());
136
0
      }
137
0
    }
138
0
  }
139
0
}