File indexing completed on 2024-05-12 04:38:06

0001 /*
0002     SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "modificationrevisionset.h"
0008 #include <debug.h>
0009 
0010 #include <serialization/itemrepository.h>
0011 #include <serialization/indexedstring.h>
0012 #include <util/setrepository.h>
0013 
0014 //When uncommented, the reason for needed updates is printed
0015 // #define DEBUG_NEEDSUPDATE
0016 
0017 namespace KDevelop {
0018 QRecursiveMutex* modificationRevisionSetMutex()
0019 {
0020     static QRecursiveMutex mutex;
0021     return &mutex;
0022 }
0023 
0024 struct FileModificationPair
0025 {
0026     KDevelop::IndexedString file;
0027     KDevelop::ModificationRevision revision;
0028 
0029     FileModificationPair()
0030     {
0031     }
0032 
0033     FileModificationPair(const KDevelop::IndexedString& _file, KDevelop::ModificationRevision _revision)
0034         : file(_file)
0035         , revision(_revision)
0036     {
0037     }
0038 
0039     FileModificationPair& operator=(const FileModificationPair& rhs) = delete;
0040 
0041     unsigned int hash() const
0042     {
0043         return ((file.hash() + revision.modificationTime) * 17 + revision.revision) * 73;
0044     }
0045 
0046     unsigned short int itemSize() const
0047     {
0048         return sizeof(FileModificationPair);
0049     }
0050 
0051     bool operator==(const FileModificationPair& rhs) const
0052     {
0053         return file == rhs.file && revision == rhs.revision;
0054     }
0055 };
0056 
0057 struct FileModificationPairRequest
0058 {
0059     FileModificationPairRequest(const FileModificationPair& data) : m_data(data)
0060     {
0061     }
0062 
0063     const FileModificationPair& m_data;
0064 
0065     enum {
0066         AverageSize = sizeof(FileModificationPair)
0067     };
0068 
0069     unsigned int hash() const
0070     {
0071         return m_data.hash();
0072     }
0073 
0074     uint itemSize() const
0075     {
0076         return m_data.itemSize();
0077     }
0078 
0079     void createItem(FileModificationPair* item) const
0080     {
0081         new (item)  FileModificationPair(m_data);
0082     }
0083 
0084     bool equals(const FileModificationPair* item) const
0085     {
0086         return *item == m_data;
0087     }
0088 
0089     static void destroy(FileModificationPair* item, KDevelop::AbstractItemRepository&)
0090     {
0091         item->~FileModificationPair();
0092     }
0093 
0094     static bool persistent(const FileModificationPair* /*item*/)
0095     {
0096         return true; //Reference-counting is done implicitly using the set-repository
0097     }
0098 };
0099 
0100 using FileModificationPairRepository
0101     = KDevelop::ItemRepository<FileModificationPair, FileModificationPairRequest, true, QRecursiveMutex>;
0102 
0103 static FileModificationPairRepository& fileModificationPairRepository()
0104 {
0105     static FileModificationPairRepository rep(QStringLiteral("file modification repository"),
0106                                               modificationRevisionSetMutex());
0107     return rep;
0108 }
0109 
0110 void initModificationRevisionSetRepository()
0111 {
0112     fileModificationPairRepository();
0113 }
0114 
0115 QHash<uint, std::pair<QDateTime, bool>> needsUpdateCache;
0116 
0117 void ModificationRevisionSet::clearCache()
0118 {
0119     QMutexLocker lock(modificationRevisionSetMutex());
0120     ///@todo More intelligent clearing. We actually need to watch the directory for changes, and if there are changes, clear the cache.
0121     needsUpdateCache.clear();
0122 }
0123 
0124 struct FileModificationSetRepository
0125     : public Utils::BasicSetRepository
0126 {
0127     FileModificationSetRepository()
0128         : Utils::BasicSetRepository(QStringLiteral("file modification sets"), modificationRevisionSetMutex(),
0129                                     &globalItemRepositoryRegistry(), true)
0130     {
0131     }
0132     void itemRemovedFromSets(uint index) override;
0133 };
0134 
0135 struct FileModificationSetRepositoryRepresenter
0136 {
0137     static FileModificationSetRepository& repository()
0138     {
0139         static FileModificationSetRepository fileModificationSetRepository;
0140         return fileModificationSetRepository;
0141     }
0142 };
0143 
0144 ModificationRevisionSet::ModificationRevisionSet(unsigned int index) : m_index(index)
0145 {
0146 }
0147 
0148 uint ModificationRevisionSet::size() const
0149 {
0150     Utils::Set set = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0151     return set.count();
0152 }
0153 
0154 void ModificationRevisionSet::clear()
0155 {
0156     QMutexLocker lock(modificationRevisionSetMutex());
0157 
0158     if (m_index) {
0159         Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0160         oldModificationTimes.staticUnref();
0161         m_index = 0;
0162     }
0163 }
0164 
0165 void ModificationRevisionSet::addModificationRevision(const IndexedString& url,
0166                                                       const KDevelop::ModificationRevision& revision)
0167 {
0168     QMutexLocker lock(modificationRevisionSetMutex());
0169 
0170     if (m_index == 0) {
0171         Utils::Set set = FileModificationSetRepositoryRepresenter::repository().createSet(
0172             fileModificationPairRepository().index(FileModificationPair(url, revision)));
0173         set.staticRef();
0174         m_index = set.setIndex();
0175     } else {
0176         Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0177         Utils::Set newModificationTimes = oldModificationTimes;
0178 
0179         Utils::Set tempSet = FileModificationSetRepositoryRepresenter::repository().createSet(
0180             fileModificationPairRepository().index(FileModificationPair(url, revision)));
0181         tempSet.staticRef();
0182 
0183         newModificationTimes += tempSet;
0184         newModificationTimes.staticRef();
0185         oldModificationTimes.staticUnref();
0186         tempSet.staticUnref();
0187 
0188         m_index = newModificationTimes.setIndex();
0189     }
0190 }
0191 
0192 bool ModificationRevisionSet::removeModificationRevision(const IndexedString& url,
0193                                                          const KDevelop::ModificationRevision& revision)
0194 {
0195     QMutexLocker lock(modificationRevisionSetMutex());
0196 
0197     if (!m_index)
0198         return false;
0199 
0200     Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0201     Utils::Set newModificationTimes = oldModificationTimes;
0202 
0203     Utils::Set tempSet = FileModificationSetRepositoryRepresenter::repository().createSet(
0204         fileModificationPairRepository().index(FileModificationPair(url, revision)));
0205     tempSet.staticRef();
0206 
0207     newModificationTimes -= tempSet;
0208     newModificationTimes.staticRef();
0209     oldModificationTimes.staticUnref();
0210     tempSet.staticUnref();
0211 
0212     m_index = newModificationTimes.setIndex();
0213     return m_index != oldModificationTimes.setIndex();
0214 }
0215 
0216 // const QMap<IndexedString, KDevelop::ModificationRevision> ModificationRevisionSet::allModificationTimes() const {
0217 //   QMap<IndexedString, KDevelop::ModificationRevision> ret;
0218 //   Utils::Set::Iterator it = m_allModificationTimes.iterator();
0219 //   while(it) {
0220 //     const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(*it);
0221 //     ret[data->file] = data->revision;
0222 //     ++it;
0223 //   }
0224 //   return ret;
0225 // }
0226 
0227 using ModificationRevisionSetNode = Utils::VirtualSetNode<uint, Utils::IdentityConversion<uint>,
0228     FileModificationSetRepositoryRepresenter>;
0229 // static bool (const Utils::SetNodeData* node) {
0230 //   ModificationRevisionSetNode
0231 //   if(!node)
0232 //     return false;
0233 // }
0234 
0235 static bool nodeNeedsUpdate(uint index)
0236 {
0237     QMutexLocker lock(modificationRevisionSetMutex());
0238 
0239     if (!index)
0240         return false;
0241 
0242     const auto currentTime = QDateTime::currentDateTimeUtc();
0243 
0244     auto cached = needsUpdateCache.constFind(index);
0245     if (cached != needsUpdateCache.constEnd()) {
0246         if ((*cached).first.secsTo(currentTime) < cacheModificationTimesForSeconds) {
0247             return cached->second;
0248         }
0249     }
0250 
0251     bool result = false;
0252 
0253     const Utils::SetNodeData* nodeData = FileModificationSetRepositoryRepresenter::repository().nodeFromIndex(index);
0254     if (nodeData->contiguous()) {
0255         //Do  the actual checking
0256         for (unsigned int a = nodeData->start(); a < nodeData->end(); ++a) {
0257             const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(a);
0258             ModificationRevision revision = KDevelop::ModificationRevision::revisionForFile(data->file);
0259             if (revision != data->revision) {
0260                 result = true;
0261                 break;
0262             }
0263         }
0264     } else {
0265         result = nodeNeedsUpdate(nodeData->leftNode()) || nodeNeedsUpdate(nodeData->rightNode());
0266     }
0267 
0268     needsUpdateCache.insert(index, std::make_pair(currentTime, result));
0269 
0270     return result;
0271 }
0272 
0273 QString ModificationRevisionSet::toString() const
0274 {
0275     QMutexLocker lock(modificationRevisionSetMutex());
0276     Utils::Set set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0277     Utils::Set::Iterator it = set.iterator();
0278     QStringList revisions;
0279     while (it) {
0280         const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(*it);
0281         revisions.append(data->file.str() + QLatin1Char(':') + data->revision.toString());
0282         ++it;
0283     }
0284 
0285     QString ret = QLatin1Char('[') + revisions.join(QLatin1String(", ")) + QLatin1Char(']');
0286     return ret;
0287 }
0288 
0289 bool ModificationRevisionSet::needsUpdate() const
0290 {
0291     QMutexLocker lock(modificationRevisionSetMutex());
0292 
0293   #ifdef DEBUG_NEEDSUPDATE
0294     Utils::Set set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0295     Utils::Set::Iterator it = set.iterator();
0296     while (it) {
0297         const FileModificationPair* data = fileModificationPairRepository().itemFromIndex(*it);
0298         ModificationRevision revision = KDevelop::ModificationRevision::revisionForFile(data->file);
0299         if (revision != data->revision) {
0300             qCDebug(LANGUAGE) << "dependency" << data->file.str() << "has changed, stored stamp:" << data->revision <<
0301                 "new time:" << revision;
0302             return true;
0303         }
0304         ++it;
0305     }
0306     return false;
0307   #else
0308     return nodeNeedsUpdate(m_index);
0309   #endif
0310 }
0311 
0312 ModificationRevisionSet& ModificationRevisionSet::operator+=(const ModificationRevisionSet& rhs)
0313 {
0314     QMutexLocker lock(modificationRevisionSetMutex());
0315 
0316     Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0317     Utils::Set otherModificationTimes =
0318         Utils::Set(rhs.m_index, &FileModificationSetRepositoryRepresenter::repository());
0319 
0320     Utils::Set newModificationTimes = oldModificationTimes;
0321 
0322     newModificationTimes += otherModificationTimes;
0323     newModificationTimes.staticRef();
0324     oldModificationTimes.staticUnref();
0325 
0326     m_index = newModificationTimes.setIndex();
0327 
0328     return *this;
0329 }
0330 
0331 ModificationRevisionSet& ModificationRevisionSet::operator-=(const ModificationRevisionSet& rhs)
0332 {
0333     QMutexLocker lock(modificationRevisionSetMutex());
0334 
0335     Utils::Set oldModificationTimes = Utils::Set(m_index, &FileModificationSetRepositoryRepresenter::repository());
0336     Utils::Set otherModificationTimes =
0337         Utils::Set(rhs.m_index, &FileModificationSetRepositoryRepresenter::repository());
0338 
0339     Utils::Set newModificationTimes = oldModificationTimes;
0340 
0341     newModificationTimes -= otherModificationTimes;
0342     newModificationTimes.staticRef();
0343     oldModificationTimes.staticUnref();
0344 
0345     m_index = newModificationTimes.setIndex();
0346 
0347     return *this;
0348 }
0349 
0350 void FileModificationSetRepository::itemRemovedFromSets(uint index)
0351 {
0352     fileModificationPairRepository().deleteItem(index);
0353     needsUpdateCache.remove(index);
0354 }
0355 }