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