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

0001 /*
0002     SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #ifndef KDEVPLATFORM_SETREPOSITORY_H
0008 #define KDEVPLATFORM_SETREPOSITORY_H
0009 
0010 #include "basicsetrepository.h"
0011 #include <QMutex>
0012 #include <list>
0013 
0014 /**
0015  * This header defines convenience-class that allow handling set-repositories using the represented higher-level objects instead
0016  * of indices to them.
0017  * */
0018 
0019 namespace Utils {
0020 /**
0021  * Use this class to conveniently iterate over the items in a set.
0022  * @tparam T The type the indices will be converted to
0023  * @tparam Conversion Should be a class that has a toIndex member function that takes an object of type T as parameter, and returns an index,
0024  *                   and a toItem member function that takes an index, and returns an item of type T.
0025  * */
0026 template <class T, class Conversion>
0027 class ConvenientIterator
0028     : public Conversion
0029 {
0030 public:
0031     explicit ConvenientIterator(const Set::Iterator& it = Set::Iterator()) : m_it(it)
0032     {
0033     }
0034     explicit ConvenientIterator(const Set& set) : m_it(set.iterator())
0035     {
0036     }
0037 
0038     operator bool() const {
0039         return m_it;
0040     }
0041 
0042     ConvenientIterator& operator++()
0043     {
0044         ++m_it;
0045         return *this;
0046     }
0047 
0048     T operator*() const
0049     {
0050         return Conversion::toItem(*m_it);
0051     }
0052 
0053     uint index() const
0054     {
0055         return *m_it;
0056     }
0057 
0058 private:
0059     Set::Iterator m_it;
0060 };
0061 
0062 struct DummyLocker
0063 {
0064 };
0065 
0066 template <class T>
0067 struct IdentityConversion
0068 {
0069     static T toIndex(const T& t)
0070     {
0071         return t;
0072     }
0073     static T toItem(const T& t)
0074     {
0075         return t;
0076     }
0077 };
0078 
0079 ///This is a virtual set-node that allows conveniently iterating through the tree-structure,
0080 ///accessing the contained items directly, and accessing the ranges.
0081 template <class T, class Conversion, class StaticRepository>
0082 class VirtualSetNode
0083 {
0084 private:
0085     using ClassType = VirtualSetNode<T, Conversion, StaticRepository>;
0086 
0087 public:
0088     inline explicit VirtualSetNode(const SetNodeData* data = nullptr) : m_data(data)
0089     {
0090     }
0091 
0092     inline bool isValid() const
0093     {
0094         return ( bool )m_data;
0095     }
0096 
0097     ///If this returns false, a left and a right node are available.
0098     ///If this returns true, this node represents a single item, that can be retrieved by calling item() or operator*.
0099     inline bool isFinalNode() const
0100     {
0101         return m_data->leftNode() == 0;
0102     }
0103 
0104     inline T firstItem() const
0105     {
0106         return Conversion::toItem(start());
0107     }
0108 
0109     inline T lastItem() const
0110     {
0111         return Conversion::toItem(end() - 1);
0112     }
0113 
0114     inline T operator*() const
0115     {
0116         return Conversion::toItem(start());
0117     }
0118 
0119     inline ClassType leftChild() const
0120     {
0121         if (m_data->leftNode())
0122             return ClassType(StaticRepository::repository()->nodeFromIndex(m_data->leftNode()));
0123         else
0124             return ClassType(nullptr);
0125     }
0126 
0127     inline ClassType rightChild() const
0128     {
0129         if (m_data->rightNode())
0130             return ClassType(StaticRepository::repository()->nodeFromIndex(m_data->rightNode()));
0131         else
0132             return ClassType(nullptr);
0133     }
0134 
0135     ///Returns the start of this node's range. If this is a final node, the length of the range is 1.
0136     inline uint start() const
0137     {
0138         return m_data->start();
0139     }
0140 
0141     ///Returns the end of this node's range.
0142     inline uint end() const
0143     {
0144         return m_data->end();
0145     }
0146 
0147 private:
0148 
0149     const SetNodeData* m_data;
0150 };
0151 
0152 template <class T, class Conversion, class StaticRepository, bool doReferenceCounting = false,
0153     class StaticAccessLocker = DummyLocker>
0154 class StorableSet
0155     : public Conversion
0156 {
0157 public:
0158 
0159     using Node = VirtualSetNode<T, Conversion, StaticRepository>;
0160 
0161     StorableSet(const StorableSet& rhs) : m_setIndex(rhs.m_setIndex)
0162     {
0163         StaticAccessLocker lock;
0164         Q_UNUSED(lock);
0165         if (doReferenceCounting)
0166             set().staticRef();
0167     }
0168 
0169     explicit StorableSet(const std::set<uint>& indices)
0170     {
0171         StaticAccessLocker lock;
0172         Q_UNUSED(lock);
0173         m_setIndex = StaticRepository::repository()->createSet(indices).setIndex();
0174         if (doReferenceCounting)
0175             set().staticRef();
0176     }
0177 
0178     StorableSet()
0179     {
0180     }
0181 
0182     ~StorableSet()
0183     {
0184         StaticAccessLocker lock;
0185         Q_UNUSED(lock);
0186         if (doReferenceCounting)
0187             set().staticUnref();
0188     }
0189 
0190     void insert(const T& t)
0191     {
0192         insertIndex(Conversion::toIndex(t));
0193     }
0194 
0195     bool isEmpty() const
0196     {
0197         return m_setIndex == 0;
0198     }
0199 
0200     uint count() const
0201     {
0202         return set().count();
0203     }
0204 
0205     void insertIndex(uint index)
0206     {
0207         StaticAccessLocker lock;
0208         Q_UNUSED(lock);
0209         Set set(m_setIndex, StaticRepository::repository());
0210         Set oldSet(set);
0211         Set addedSet = StaticRepository::repository()->createSet(index);
0212         if (doReferenceCounting)
0213             addedSet.staticRef();
0214         set += addedSet;
0215         m_setIndex = set.setIndex();
0216 
0217         if (doReferenceCounting) {
0218             set.staticRef();
0219             oldSet.staticUnref();
0220             addedSet.staticUnref();
0221         }
0222     }
0223 
0224     void remove(const T& t)
0225     {
0226         removeIndex(Conversion::toIndex(t));
0227     }
0228 
0229     void removeIndex(uint index)
0230     {
0231         StaticAccessLocker lock;
0232         Q_UNUSED(lock);
0233         Set set(m_setIndex, StaticRepository::repository());
0234         Set oldSet(set);
0235         Set removedSet = StaticRepository::repository()->createSet(index);
0236         if (doReferenceCounting) {
0237             removedSet.staticRef();
0238         }
0239         set -= removedSet;
0240         m_setIndex = set.setIndex();
0241 
0242         if (doReferenceCounting) {
0243             set.staticRef();
0244             oldSet.staticUnref();
0245             removedSet.staticUnref();
0246         }
0247     }
0248 
0249     Set set() const
0250     {
0251         return Set(m_setIndex, StaticRepository::repository());
0252     }
0253 
0254     bool contains(const T& item) const
0255     {
0256         return containsIndex(Conversion::toIndex(item));
0257     }
0258 
0259     bool containsIndex(uint index) const
0260     {
0261         StaticAccessLocker lock;
0262         Q_UNUSED(lock);
0263         Set set(m_setIndex, StaticRepository::repository());
0264         return set.contains(index);
0265     }
0266 
0267     StorableSet& operator +=(const StorableSet& rhs)
0268     {
0269         StaticAccessLocker lock;
0270         Q_UNUSED(lock);
0271         Set set(m_setIndex, StaticRepository::repository());
0272         Set oldSet(set);
0273         Set otherSet(rhs.m_setIndex, StaticRepository::repository());
0274         set += otherSet;
0275         m_setIndex = set.setIndex();
0276 
0277         if (doReferenceCounting) {
0278             set.staticRef();
0279             oldSet.staticUnref();
0280         }
0281         return *this;
0282     }
0283 
0284     StorableSet& operator -=(const StorableSet& rhs)
0285     {
0286         StaticAccessLocker lock;
0287         Q_UNUSED(lock);
0288         Set set(m_setIndex, StaticRepository::repository());
0289         Set oldSet(set);
0290         Set otherSet(rhs.m_setIndex, StaticRepository::repository());
0291         set -= otherSet;
0292         m_setIndex = set.setIndex();
0293 
0294         if (doReferenceCounting) {
0295             set.staticRef();
0296             oldSet.staticUnref();
0297         }
0298         return *this;
0299     }
0300 
0301     StorableSet& operator &=(const StorableSet& rhs)
0302     {
0303         StaticAccessLocker lock;
0304         Q_UNUSED(lock);
0305         Set set(m_setIndex, StaticRepository::repository());
0306         Set oldSet(set);
0307         Set otherSet(rhs.m_setIndex, StaticRepository::repository());
0308         set &= otherSet;
0309         m_setIndex = set.setIndex();
0310 
0311         if (doReferenceCounting) {
0312             set.staticRef();
0313             oldSet.staticUnref();
0314         }
0315         return *this;
0316     }
0317 
0318     StorableSet& operator=(const StorableSet& rhs)
0319     {
0320         StaticAccessLocker lock;
0321         Q_UNUSED(lock);
0322         if (doReferenceCounting)
0323             set().staticUnref();
0324         m_setIndex = rhs.m_setIndex;
0325         if (doReferenceCounting)
0326             set().staticRef();
0327         return *this;
0328     }
0329 
0330     StorableSet operator +(const StorableSet& rhs) const
0331     {
0332         StorableSet ret(*this);
0333         ret += rhs;
0334         return ret;
0335     }
0336 
0337     StorableSet operator -(const StorableSet& rhs) const
0338     {
0339         StorableSet ret(*this);
0340         ret -= rhs;
0341         return ret;
0342     }
0343 
0344     StorableSet operator &(const StorableSet& rhs) const
0345     {
0346         StorableSet ret(*this);
0347         ret &= rhs;
0348         return ret;
0349     }
0350 
0351     bool operator==(const StorableSet& rhs) const
0352     {
0353         return m_setIndex == rhs.m_setIndex;
0354     }
0355 
0356     using Iterator = ConvenientIterator<T, Conversion>;
0357 
0358     Iterator iterator() const
0359     {
0360         return ConvenientIterator<T, Conversion>(set());
0361     }
0362 
0363     Node node() const
0364     {
0365         return Node(StaticRepository::repository()->nodeFromIndex(m_setIndex));
0366     }
0367 
0368     uint setIndex() const
0369     {
0370         return m_setIndex;
0371     }
0372 
0373 private:
0374 
0375     uint m_setIndex = 0;
0376 };
0377 
0378 template <class T, class Conversion, class StaticRepository, bool doReferenceCounting, class StaticAccessLocker>
0379 uint qHash(const StorableSet<T, Conversion, StaticRepository, doReferenceCounting, StaticAccessLocker>& set)
0380 {
0381     return set.setIndex();
0382 }
0383 /** This is a helper-class that helps inserting a bunch of items into a set without caring about grouping them together.
0384  *
0385  * It creates a much better tree-structure if many items are inserted at one time, and this class helps doing that in
0386  * cases where there is no better choice then storing a temporary list of items and inserting them all at once.
0387  *
0388  * This set will then care about really inserting them into the repository once the real set is requested.
0389  *
0390  * @todo eventually make this unnecessary
0391  *
0392  * @tparam T Should be the type that should be dealt
0393  * @tparam Conversion Should be a class that has a toIndex member function that takes an object of type T as parameter, and returns an index,
0394  *                   and a toItem member function that takes an index, and returns an item of type T.
0395  **/
0396 template <class T, class Conversion>
0397 class LazySet
0398     : public Conversion
0399 {
0400 public:
0401     /** @param rep The repository the set should belong/belongs to
0402      *  @param lockBeforeAccess If this is nonzero, the given mutex will be locked before each modification to the repository.
0403      *  @param basicSet If this is explicitly given, the given set will be used as base. However it will not be changed.
0404      *
0405      * @warning Watch for deadlocks, never use this class while the mutex given through lockBeforeAccess is locked
0406      */
0407     explicit LazySet(BasicSetRepository* rep, QMutex* lockBeforeAccess = nullptr, const Set& basicSet = Set()) : m_rep(
0408             rep)
0409         , m_set(basicSet)
0410         , m_lockBeforeAccess(lockBeforeAccess)
0411     {
0412     }
0413 
0414     void insert(const T& t)
0415     {
0416         if (!m_temporaryRemoveIndices.empty())
0417             apply();
0418         m_temporaryIndices.insert(Conversion::toIndex(t));
0419     }
0420 
0421     void insertIndex(uint index)
0422     {
0423         if (!m_temporaryRemoveIndices.empty())
0424             apply();
0425         m_temporaryIndices.insert(index);
0426     }
0427 
0428     void remove(const T& t)
0429     {
0430         if (!m_temporaryIndices.empty())
0431             apply();
0432         m_temporaryRemoveIndices.insert(Conversion::toIndex(t));
0433     }
0434 
0435     ///Returns the set this LazySet represents. When this is called, the set is constructed in the repository.
0436     Set set() const
0437     {
0438         apply();
0439         return m_set;
0440     }
0441 
0442     ///@warning this is expensive, because the set is constructed
0443     bool contains(const T& item) const
0444     {
0445         QMutexLocker l(m_lockBeforeAccess);
0446         uint index = Conversion::toIndex(item);
0447 
0448         if (m_temporaryRemoveIndices.empty()) {
0449             //Simplification without creating the set
0450             if (m_temporaryIndices.find(index) != m_temporaryIndices.end())
0451                 return true;
0452 
0453             return m_set.contains(index);
0454         }
0455 
0456         return set().contains(index);
0457     }
0458 
0459     LazySet& operator +=(const Set& set)
0460     {
0461         if (!m_temporaryRemoveIndices.empty())
0462             apply();
0463         QMutexLocker l(m_lockBeforeAccess);
0464         m_set += set;
0465         return *this;
0466     }
0467 
0468     LazySet& operator -=(const Set& set)
0469     {
0470         if (!m_temporaryIndices.empty())
0471             apply();
0472         QMutexLocker l(m_lockBeforeAccess);
0473         m_set -= set;
0474         return *this;
0475     }
0476 
0477     LazySet operator +(const Set& set) const
0478     {
0479         apply();
0480         QMutexLocker l(m_lockBeforeAccess);
0481         Set ret = m_set + set;
0482         return LazySet(m_rep, m_lockBeforeAccess, ret);
0483     }
0484 
0485     LazySet operator -(const Set& set) const
0486     {
0487         apply();
0488         QMutexLocker l(m_lockBeforeAccess);
0489         Set ret = m_set - set;
0490         return LazySet(m_rep, m_lockBeforeAccess, ret);
0491     }
0492 
0493     void clear()
0494     {
0495         QMutexLocker l(m_lockBeforeAccess);
0496         m_set = Set();
0497         m_temporaryIndices.clear();
0498         m_temporaryRemoveIndices.clear();
0499     }
0500 
0501     ConvenientIterator<T, Conversion> iterator() const
0502     {
0503         apply();
0504         return ConvenientIterator<T, Conversion>(set());
0505     }
0506 
0507 private:
0508     void apply() const
0509     {
0510         if (!m_temporaryIndices.empty()) {
0511             QMutexLocker l(m_lockBeforeAccess);
0512             Set tempSet = m_rep->createSet(m_temporaryIndices);
0513             m_temporaryIndices.clear();
0514             m_set += tempSet;
0515         }
0516         if (!m_temporaryRemoveIndices.empty()) {
0517             QMutexLocker l(m_lockBeforeAccess);
0518             Set tempSet = m_rep->createSet(m_temporaryRemoveIndices);
0519             m_temporaryRemoveIndices.clear();
0520             m_set -= tempSet;
0521         }
0522     }
0523     BasicSetRepository* m_rep;
0524     mutable Set m_set;
0525     QMutex* m_lockBeforeAccess;
0526     using IndexList = std::set<Utils::BasicSetRepository::Index>;
0527     mutable IndexList m_temporaryIndices;
0528     mutable IndexList m_temporaryRemoveIndices;
0529 };
0530 
0531 ///Persistent repository that manages string-sets, also correctly increasing the string reference-counts as needed
0532 struct KDEVPLATFORMLANGUAGE_EXPORT StringSetRepository
0533     : public Utils::BasicSetRepository
0534 {
0535     explicit StringSetRepository(const QString& name, QRecursiveMutex* mutex);
0536     void itemRemovedFromSets(uint index) override;
0537     void itemAddedToSets(uint index) override;
0538 };
0539 }
0540 
0541 #endif