diff options
| author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2024-12-05 12:58:20 +0100 |
|---|---|---|
| committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2024-12-09 15:08:12 +0100 |
| commit | 3da05cc765ab16b3622e2262022a40aa92fc0334 (patch) | |
| tree | 247aa30bb678a8a3330e1bd60e7021a8452182e7 /sources | |
| parent | c76ffbfaf1e9257b9585ff935a33d334e3772892 (diff) | |
shiboken6: Prepare the graph used for topological for sorting classes without operator==()
The graph used an array of Node to represent edges, which is
suitable only for small classes that have operator==().
Change the internal representation to use indexes for edges instead of
the node classes and add an API for adding edges by index. Rewrite the
visit() algorithm to create an index list instead of a node list. Move
the Node from NodeEntry to a separate node list, which allows for
copying it directly from the node list passed in.
Task-number: PYSIDE-2946
Task-number: PYSIDE-454
Pick-to: 6.8
Change-Id: I7becd5caef226f50b77f5def2a62a2620d39ef8e
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources')
| -rw-r--r-- | sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp | 2 | ||||
| -rw-r--r-- | sources/shiboken6/ApiExtractor/graph.h | 145 |
2 files changed, 89 insertions, 58 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp index 45fa93906..b1b6fa764 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp @@ -3543,7 +3543,7 @@ static QList<std::shared_ptr<MetaClass> > topologicalSortHelper(const QList<std::shared_ptr<MetaClass> > &classList, const Dependencies &additionalDependencies) { - Graph<std::shared_ptr<MetaClass> > graph(classList.cbegin(), classList.cend()); + Graph<std::shared_ptr<MetaClass> > graph(classList); for (const auto &dep : additionalDependencies) { if (!graph.addEdge(dep.parent, dep.child)) { diff --git a/sources/shiboken6/ApiExtractor/graph.h b/sources/shiboken6/ApiExtractor/graph.h index fa617dc1e..22936c201 100644 --- a/sources/shiboken6/ApiExtractor/graph.h +++ b/sources/shiboken6/ApiExtractor/graph.h @@ -34,14 +34,28 @@ struct GraphSortResult template <class Node> class Graph { + using IndexList = QList<qsizetype>; + public: using NodeList = QList<Node>; Graph() = default; - // Construct from a sequence of nodes + // Construct from a QList of nodes (unchecked, does not require operator==()) + explicit Graph(const NodeList &list) : m_nodes(list) + { + m_nodeEntries.resize(list.size()); + } + + // Construct from a sequence of nodes (checks for duplicated nodes using operator==()) template<class It> - explicit Graph(It i1, It i2) { setNodes(i1, i2); } + explicit Graph(It i1, It i2) + { + const auto size = std::distance(i1, i2); + m_nodes.reserve(size); + m_nodeEntries.reserve(size); + setNodes(i1, i2); + } template<class It> void setNodes(It i1, It i2) @@ -53,22 +67,29 @@ public: bool addNode(Node n); /// Returns whether node was registered - bool hasNode(Node node) { return indexOfNode(node) != -1; } + bool hasNode(Node node) { return m_nodes.contains(node); } /// Returns the numbed of nodes in this graph. qsizetype nodeCount() const { return m_nodeEntries.size(); } /// Returns true if the graph contains the edge from -> to bool containsEdge(Node from, Node to) const; + bool containsEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) const; /// Returns true if the graph has any edges bool hasEdges() const; /// Adds an edge to this graph. bool addEdge(Node from, Node to); + bool addEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex); /// Removes an edge from this graph. bool removeEdge(Node from, Node to); + bool removeEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex); /// Clears the graph - void clear() { m_nodeEntries.clear(); } + void clear() + { + m_nodes.clear(); + m_nodeEntries.clear(); + } /// Dumps a dot graph to a file named \p filename. /// \param fileName file name where the output should be written. @@ -94,45 +115,33 @@ private: struct NodeEntry { - Node node; - NodeList targets; - mutable Color color; + IndexList targets; + mutable Color color = WHITE; }; Color colorAt(qsizetype i) const { return m_nodeEntries.at(i).color; } - qsizetype indexOfNode(Node n) const; - void depthFirstVisit(qsizetype i, NodeList &result) const; + void depthFirstVisit(qsizetype i, IndexList *result) const; + NodeList m_nodes; QList<NodeEntry> m_nodeEntries; }; template <class Node> -qsizetype Graph<Node>::indexOfNode(Node n) const -{ - for (qsizetype i = 0, size = m_nodeEntries.size(); i < size; ++i) { - if (m_nodeEntries.at(i).node == n) - return i; - } - return -1; -} - -template <class Node> bool Graph<Node>::addNode(Node n) { if (hasNode(n)) return false; - m_nodeEntries.append({n, {}, WHITE}); + m_nodes.append(n); + m_nodeEntries.append(NodeEntry{}); return true; } template <class Node> -void Graph<Node>::depthFirstVisit(qsizetype i, NodeList &result) const +void Graph<Node>::depthFirstVisit(qsizetype i, IndexList *result) const { m_nodeEntries[i].color = GRAY; - for (auto to : m_nodeEntries[i].targets) { - const qsizetype toIndex = indexOfNode(to); - Q_ASSERT(toIndex != -1); + for (qsizetype toIndex : m_nodeEntries.at(i).targets) { switch (colorAt(toIndex)) { case WHITE: depthFirstVisit(toIndex, result); @@ -146,7 +155,7 @@ void Graph<Node>::depthFirstVisit(qsizetype i, NodeList &result) const m_nodeEntries[i].color = BLACK; - result.append(m_nodeEntries.at(i).node); + result->append(i); } template <class Node> @@ -155,29 +164,29 @@ GraphSortResult<Node> Graph<Node>::topologicalSort() const const qsizetype size = m_nodeEntries.size(); GraphSortResult<Node> result; - result.result.reserve(size); if (size > 1 && hasEdges()) { + result.result.reserve(size); + IndexList indexList; + indexList.reserve(size); for (qsizetype i = 0; i < size; ++i) m_nodeEntries[i].color = WHITE; for (qsizetype i = 0; i < size; ++i) { if (colorAt(i) == WHITE) // recursive calls may have set it to black - depthFirstVisit(i, result.result); + depthFirstVisit(i, &indexList); } - } else { // no edges, shortcut - std::transform(m_nodeEntries.cbegin(), m_nodeEntries.cend(), - std::back_inserter(result.result), - [](const NodeEntry &ne) { return ne.node; }); - } - - if (result.result.size() == size) { - std::reverse(result.result.begin(), result.result.end()); - } else { - for (const auto &nodeEntry : m_nodeEntries) { - if (!result.result.contains(nodeEntry.node)) - result.cyclic.append(nodeEntry.node); + if (indexList.size() == size) { // Succeeded, all traversed + for (qsizetype i = size - 1; i >= 0; --i) + result.result.append(m_nodes.at(indexList.at(i))); + } else { // Cyclic, Not a DAG! + for (qsizetype i = 0; i < size; ++i) { + if (!indexList.contains(i)) + result.cyclic.append(m_nodes.at(i)); + } } - result.result.clear(); // Not a DAG! + } else { // no edges, shortcut. Legacy behavior: Also reverse in this case. + result.result = m_nodes; + std::reverse(result.result.begin(), result.result.end()); } return result; } @@ -185,8 +194,14 @@ GraphSortResult<Node> Graph<Node>::topologicalSort() const template <class Node> bool Graph<Node>::containsEdge(Node from, Node to) const { - const qsizetype i = indexOfNode(from); - return i != -1 && m_nodeEntries.at(i).targets.contains(to); + return containsEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to)); +} + +template <class Node> +bool Graph<Node>::containsEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) const +{ + return fromIndex >= 0 && fromIndex < m_nodeEntries.size() + && m_nodeEntries.at(fromIndex).targets.contains(toIndex); } template <class Node> @@ -199,24 +214,39 @@ bool Graph<Node>::hasEdges() const template <class Node> bool Graph<Node>::addEdge(Node from, Node to) { - const qsizetype i = indexOfNode(from); - if (i == -1 || !hasNode(to) || m_nodeEntries.at(i).targets.contains(to)) + return addEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to)); +} + +template <class Node> +bool Graph<Node>::addEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) +{ + if (fromIndex < 0 || fromIndex >= m_nodeEntries.size() + || toIndex < 0 || toIndex >= m_nodeEntries.size() + || m_nodeEntries.at(fromIndex).targets.contains(toIndex)) { return false; - m_nodeEntries[i].targets.append(to); + } + m_nodeEntries[fromIndex].targets.append(toIndex); return true; } template <class Node> bool Graph<Node>::removeEdge(Node from, Node to) { - const qsizetype i = indexOfNode(from); - if (i == -1) + return removeEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to)); +} + +template <class Node> +bool Graph<Node>::removeEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) +{ + if (fromIndex < 0 || fromIndex >= m_nodeEntries.size() + || toIndex < 0 || toIndex >= m_nodeEntries.size()) { return false; - auto &targets = m_nodeEntries[i].targets; - const qsizetype toIndex = targets.indexOf(to); - if (toIndex == -1) + } + auto &targets = m_nodeEntries[fromIndex].targets; + const qsizetype toPos = targets.indexOf(toIndex); + if (toPos == -1) return false; - targets.removeAt(toIndex); + targets.removeAt(toPos); return true; } @@ -239,11 +269,12 @@ void Graph<Node>::formatDot(QTextStream &s, NameFunction nameFunction) const { s << "digraph D {\n"; - for (const auto &nodeEntry : m_nodeEntries) { + for (qsizetype i = 0, size = m_nodes.size(); i < size; ++i) { + const auto &nodeEntry = m_nodeEntries.at(i); if (!nodeEntry.targets.isEmpty()) { - const QString fromName = nameFunction(nodeEntry.node); - for (const Node &to : nodeEntry.targets) - s << '"' << fromName << "\" -> \"" << nameFunction(to) << "\"\n"; + const QString fromName = nameFunction(m_nodes.at(i)); + for (qsizetype i : nodeEntry.targets) + s << '"' << fromName << "\" -> \"" << nameFunction(m_nodes.at(i)) << "\"\n"; } } s << "}\n"; @@ -268,13 +299,13 @@ void Graph<Node>::format(QDebug &debug) const const auto &nodeEntry = m_nodeEntries.at(i); if (i) debug << ", "; - debug << nodeEntry.node; + debug << m_nodes.at(i); if (!nodeEntry.targets.isEmpty()) { debug << " -> ["; for (qsizetype t = 0, tsize = nodeEntry.targets.size(); t < tsize; ++t) { if (t) debug << ", "; - debug << nodeEntry.targets.at(t); + debug << m_nodes.at(nodeEntry.targets.at(t)); } debug << ']'; } |
