src/documentmanager.cpp

changeset 213
ee5758ddb6d2
parent 212
27259810da6d
child 214
8e1fe64ce4e3
--- a/src/documentmanager.cpp	Sat Jun 11 14:30:30 2022 +0300
+++ b/src/documentmanager.cpp	Sat Jun 11 15:20:24 2022 +0300
@@ -20,6 +20,7 @@
 #include <QDir>
 #include <QFileInfo>
 #include <QSaveFile>
+#include <deque>
 #include "documentmanager.h"
 #include "parser.h"
 
@@ -255,51 +256,49 @@
 		.arg(missingString);
 }
 
-/**
- * @brief Cleans up and erases models that are no longer required.
- */
-void DocumentManager::prune()
+template<typename T, typename K>
+void removeFromSet(std::set<T>& set, K&& valueToRemove)
 {
-	for (auto it = this->openModels.begin(); it != this->openModels.end(); ++it)
-	{
-		// Find models that are not edited by the user and are not needed by any other model
-		if (true
-			and it->second.opentype == OpenType::AutomaticallyOpened
-			and not this->isReferencedByAnything(it->first)
-		) {
-			// Remove the model
-			this->openModels.erase(it);
-			// We need to start over now. It is possible that other models that
-			// previously were referenced by the model we just erased have
-			// become prunable. Moreover, our iterator is invalid now and we
-			// cannot continue in this loop.
-			this->prune();
-			break;
-		}
+	const auto it = std::lower_bound(set.begin(), set.end(), valueToRemove);
+	if (it != set.end() and *it == valueToRemove) {
+		set.erase(it);
 	}
 }
 
-/**
- * @brief Finds out whether the specified model id is referenced by any other model
- * @param modelId
- * @returns bool
- */
-bool DocumentManager::isReferencedByAnything(const ModelId modelId) const
+//! @brief Cleans up and erases models that are no longer required.
+void DocumentManager::prune()
 {
-	for (auto& haystackModelPair : this->openModels)
-	{
-		if (haystackModelPair.first != modelId)
-		{
-			for (auto& dependencyPair : haystackModelPair.second.dependencies)
-			{
-				if (dependencyPair.second == modelId)
-				{
-					return true;
-				}
+	Graph<ModelId> dependencyGraph;
+	forValueInMap(this->openModels, [&dependencyGraph](const ModelInfo& info) {
+		forValueInMap(info.dependencies, [&dependencyGraph, &info](ModelId dep){
+			dependencyGraph.push_back({.from = info.id, .to = dep});
+		});
+	});
+	std::set<ModelId> autoOpened;
+	forValueInMap(this->openModels, [&autoOpened](const ModelInfo& info) {
+		if (info.opentype == OpenType::AutomaticallyOpened) {
+			autoOpened.insert(info.id);
+		}
+	});
+	bool repeat = true;
+	while (repeat) {
+		repeat = false;
+		std::set<ModelId> prunable = autoOpened;
+		for (const auto& pair : dependencyGraph) {
+			removeFromSet(prunable, pair.to);
+		}
+		for (ModelId idToPrune : prunable) {
+			auto it = this->openModels.find(idToPrune);
+			if (it != this->openModels.end()) {
+				this->openModels.erase(it);
 			}
+			removeFromSet(autoOpened, idToPrune);
+			std::erase_if(dependencyGraph, [&idToPrune](const GraphEdge<ModelId>& edge) {
+				return edge.from == idToPrune;
+			});
+			repeat = true;
 		}
 	}
-	return false;
 }
 
 void DocumentManager::makePolygonCacheForModel(const ModelId modelId)

mercurial