00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifndef ITEMREPOSITORY_H
00020 #define ITEMREPOSITORY_H
00021
00022 #include <QtCore/QString>
00023 #include <QtCore/QVector>
00024 #include <QtCore/QByteArray>
00025 #include <QtCore/QMutex>
00026 #include <QtCore/QList>
00027 #include <QtCore/QDir>
00028 #include <QtCore/QFile>
00029 #include <QtCore/QAtomicInt>
00030 #include <klockfile.h>
00031 #include <kdebug.h>
00032 #include "../../languageexport.h"
00033 #include "../referencecounting.h"
00034
00035
00036
00037
00038
00039 #define ifDebugInfiniteRecursion(x)
00040
00041
00042 #define ifDebugLostSpace(x)
00043
00044
00045
00046
00047
00049
00050 #ifdef DEBUG_ITEM_REACHABILITY
00051 #define ENSURE_REACHABLE(bucket) Q_ASSERT(allItemsReachable(bucket));
00052 #else
00053 #define ENSURE_REACHABLE(bucket)
00054 #endif
00055
00057
00058
00059 #define ITEMREPOSITORY_USE_MMAP_LOADING
00060
00061
00062
00063 #ifdef QT_NO_DEBUG
00064 #define VERIFY(X) if(!(X)) {kWarning() << "Failed to verify expression" << #X;}
00065 #else
00066 #define VERIFY(X) Q_ASSERT(X)
00067 #endif
00068
00073
00074
00075 namespace KDevelop {
00076
00092
00093 KDEVPLATFORMLANGUAGE_EXPORT uint staticItemRepositoryVersion();
00094
00095 class KDEVPLATFORMLANGUAGE_EXPORT AbstractItemRepository {
00096 public:
00097 virtual ~AbstractItemRepository();
00101 virtual bool open(const QString& path) = 0;
00102 virtual void close(bool doStore = false) = 0;
00103 virtual void store() = 0;
00105 virtual int finalCleanup() = 0;
00106 virtual QString repositoryName() const = 0;
00107 virtual QString printStatistics() const = 0;
00108 };
00109
00111 class KDEVPLATFORMLANGUAGE_EXPORT AbstractRepositoryManager {
00112 public:
00113 AbstractRepositoryManager() : m_repository(0) {
00114 }
00115
00116 virtual ~AbstractRepositoryManager() {
00117 }
00118
00119 void deleteRepository() {
00120 delete m_repository;
00121 m_repository = 0;
00122
00123 repositoryDeleted();
00124 }
00125
00126 virtual QMutex* repositoryMutex() const = 0;
00127
00128 virtual void repositoryDeleted() {
00129 }
00130
00131 protected:
00132 mutable AbstractItemRepository* m_repository;
00133 };
00134
00140 class KDEVPLATFORMLANGUAGE_EXPORT ItemRepositoryRegistry {
00141 public:
00142 ItemRepositoryRegistry(QString openPath = QString(), KLockFile::Ptr lock = KLockFile::Ptr());
00143 ~ItemRepositoryRegistry();
00144
00150 bool open(const QString& path, bool clear = false, KLockFile::Ptr lock = KLockFile::Ptr());
00152 void close();
00154 void registerRepository(AbstractItemRepository* repository, AbstractRepositoryManager* manager);
00156 void unRegisterRepository(AbstractItemRepository* repository);
00158 QString path() const;
00160 void store();
00161
00163 void shutdown();
00164
00167 int finalCleanup();
00168
00170 void printAllStatistics() const;
00171
00174 void lockForWriting();
00176 void unlockForWriting();
00177
00180 QAtomicInt& getCustomCounter(const QString& identity, int initialValue);
00181
00183 QMutex& mutex();
00184 private:
00185 void deleteDataDirectory();
00186 QString m_path;
00187 QMap<AbstractItemRepository*, AbstractRepositoryManager*> m_repositories;
00188 QMap<QString, QAtomicInt*> m_customCounters;
00189 KLockFile::Ptr m_lock;
00190 mutable QMutex m_mutex;
00191 };
00192
00194 KDEVPLATFORMLANGUAGE_EXPORT ItemRepositoryRegistry& globalItemRepositoryRegistry();
00195
00198 template<class ItemRepositoryType, bool unloadingEnabled = true, bool lazy = true>
00199 struct RepositoryManager : public AbstractRepositoryManager{
00200 public:
00202 RepositoryManager(QString name, int version = 1, AbstractRepositoryManager* (*shareMutex)() = 0, ItemRepositoryRegistry& registry = globalItemRepositoryRegistry()) : m_name(name), m_version(version), m_registry(registry), m_shareMutex(shareMutex) {
00203 if(!lazy)
00204 createRepository();
00205 }
00206
00207 ~RepositoryManager() {
00208
00209
00210 }
00211
00212 inline ItemRepositoryType* operator->() const {
00213 if(!m_repository)
00214 createRepository();
00215
00216 return static_cast<ItemRepositoryType*>(m_repository);
00217 }
00218
00219 QMutex* repositoryMutex() const {
00220 return (*this)->mutex();
00221 }
00222
00223 private:
00224
00225 void createRepository() const {
00226 if(!m_repository) {
00227 QMutexLocker lock(&m_registry.mutex());
00228 if(!m_repository) {
00229 m_repository = new ItemRepositoryType(m_name, &m_registry, m_version, const_cast<RepositoryManager*>(this));
00230 if(m_shareMutex)
00231 (*this)->setMutex(m_shareMutex()->repositoryMutex());
00232 (*this)->setUnloadingEnabled(unloadingEnabled);
00233 }
00234 }
00235 }
00236
00237 QString m_name;
00238 int m_version;
00239 mutable ItemRepositoryRegistry& m_registry;
00240 AbstractRepositoryManager* (*m_shareMutex)();
00241 };
00242
00246 class ExampleItem {
00247
00248
00249 unsigned int hash() const {
00250 return 0;
00251 }
00252
00253
00254
00255 unsigned short int itemSize() const {
00256 return 0;
00257 }
00258 };
00259
00268 enum {
00269 ItemRepositoryBucketSize = 1<<16
00270 };
00271
00272 class ExampleItemRequest {
00273
00274 enum {
00275 AverageSize = 10
00276 };
00277
00278 typedef unsigned int HashType;
00279
00281 HashType hash() const {
00282 return 0;
00283 }
00284
00286 size_t itemSize() const {
00287 return 0;
00288 }
00292 void createItem(ExampleItem* ) const {
00293 }
00294 static void destroy(ExampleItem* , AbstractItemRepository&) {
00295 }
00296
00298 static bool persistent(ExampleItem*) {
00299 return true;
00300 }
00301
00303 bool equals(const ExampleItem* ) const {
00304 return false;
00305 }
00306 };
00307
00308 template<class Item, class ItemRequest, bool markForReferenceCounting, uint fixedItemSize>
00309 class Bucket {
00310 public:
00311 enum {
00312 AdditionalSpacePerItem = 2
00313 };
00314 enum {
00315 ObjectMapSize = ((ItemRepositoryBucketSize / ItemRequest::AverageSize) * 3) / 2 + 1,
00316 MaxFreeItemsForHide = 0,
00317 MaxFreeSizeForHide = fixedItemSize ? fixedItemSize : 0,
00318 MinFreeItemsForReuse = 10,
00319 MinFreeSizeForReuse = ItemRepositoryBucketSize/20
00320 };
00321 enum {
00322 NextBucketHashSize = ObjectMapSize,
00323 DataSize = sizeof(char) + sizeof(unsigned int) * 3 + ItemRepositoryBucketSize + sizeof(short unsigned int) * (ObjectMapSize + NextBucketHashSize + 1)
00324 };
00325 enum {
00326 CheckStart = 0xff00ff1,
00327 CheckEnd = 0xfafcfb
00328 };
00329 Bucket() : m_monsterBucketExtent(0), m_available(0), m_data(0), m_mappedData(0), m_objectMap(0), m_objectMapSize(0), m_largestFreeItem(0), m_freeItemCount(0), m_nextBucketHash(0), m_dirty(false) {
00330 }
00331 ~Bucket() {
00332 if(m_data != m_mappedData) {
00333 delete[] m_data;
00334 delete[] m_nextBucketHash;
00335 delete[] m_objectMap;
00336 }
00337 }
00338
00339 void initialize(uint monsterBucketExtent) {
00340 if(!m_data) {
00341 m_monsterBucketExtent = monsterBucketExtent;
00342 m_available = ItemRepositoryBucketSize;
00343 m_data = new char[ItemRepositoryBucketSize + monsterBucketExtent * DataSize];
00344
00345 m_objectMapSize = ObjectMapSize;
00346 m_objectMap = new short unsigned int[m_objectMapSize];
00347 memset(m_objectMap, 0, m_objectMapSize * sizeof(short unsigned int));
00348 m_nextBucketHash = new short unsigned int[NextBucketHashSize];
00349 memset(m_nextBucketHash, 0, NextBucketHashSize * sizeof(short unsigned int));
00350 m_changed = true;
00351 m_dirty = false;
00352 m_lastUsed = 0;
00353 }
00354 }
00355
00356 template<class T>
00357 void readIn(char*& from, int count, T* to) {
00358 for(int a = 0; a < count; ++a) {
00359 *to = *((T*)from);
00360 ++to;
00361 from += sizeof(T);
00362 }
00363 }
00364 template<class T>
00365 void readOne(char*& from, T& to) {
00366 readIn(from, 1, &to);
00367 }
00368
00369 void initializeFromMap(char* current) {
00370 if(!m_data) {
00371 char* start = current;
00372 readOne(current, m_monsterBucketExtent);
00373 Q_ASSERT(current - start == 4);
00374 readOne(current, m_available);
00375 m_objectMapSize = ObjectMapSize;
00376 m_objectMap = (short unsigned int*)current;
00377 current += sizeof(short unsigned int) * m_objectMapSize;
00378 m_nextBucketHash = (short unsigned int*)current;
00379 current += sizeof(short unsigned int) * NextBucketHashSize;
00380 readOne<short unsigned int>(current, m_largestFreeItem);
00381 readOne<unsigned int>(current, m_freeItemCount);
00382 readOne<bool>(current, m_dirty);
00383 m_data = current;
00384 m_mappedData = current;
00385
00386 m_changed = false;
00387 m_lastUsed = 0;
00388 VERIFY(current - start == (DataSize - ItemRepositoryBucketSize));
00389 }
00390 }
00391
00392 void store(QFile* file, size_t offset) {
00393 if(!m_data)
00394 return;
00395
00396 if(static_cast<size_t>(file->size()) < offset + (1+m_monsterBucketExtent)*DataSize)
00397 file->resize(offset + (1+m_monsterBucketExtent)*DataSize);
00398
00399 file->seek(offset);
00400
00401 file->write((char*)&m_monsterBucketExtent, sizeof(unsigned int));
00402 file->write((char*)&m_available, sizeof(unsigned int));
00403 file->write((char*)m_objectMap, sizeof(short unsigned int) * m_objectMapSize);
00404 file->write((char*)m_nextBucketHash, sizeof(short unsigned int) * NextBucketHashSize);
00405 file->write((char*)&m_largestFreeItem, sizeof(short unsigned int));
00406 file->write((char*)&m_freeItemCount, sizeof(unsigned int));
00407 file->write((char*)&m_dirty, sizeof(bool));
00408 file->write(m_data, ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize);
00409
00410 Q_ASSERT(static_cast<size_t>(file->pos()) == offset + (1+m_monsterBucketExtent)*DataSize);
00411 m_changed = false;
00412 #ifdef DEBUG_ITEMREPOSITORY_LOADING
00413 {
00414 file->flush();
00415 file->seek(offset);
00416
00417 uint available, freeItemCount, monsterBucketExtent;
00418 short unsigned int largestFree;
00419 bool dirty;
00420
00421 short unsigned int* m = new short unsigned int[m_objectMapSize];
00422 short unsigned int* h = new short unsigned int[NextBucketHashSize];
00423
00424
00425 file->read((char*)&monsterBucketExtent, sizeof(unsigned int));
00426 char* d = new char[ItemRepositoryBucketSize + monsterBucketExtent * DataSize];
00427
00428 file->read((char*)&available, sizeof(unsigned int));
00429 file->read((char*)m, sizeof(short unsigned int) * m_objectMapSize);
00430 file->read((char*)h, sizeof(short unsigned int) * NextBucketHashSize);
00431 file->read((char*)&largestFree, sizeof(short unsigned int));
00432 file->read((char*)&freeItemCount, sizeof(unsigned int));
00433 file->read((char*)&dirty, sizeof(bool));
00434 file->read(d, ItemRepositoryBucketSize);
00435
00436 Q_ASSERT(monsterBucketExtent == m_monsterBucketExtent);
00437 Q_ASSERT(available == m_available);
00438 Q_ASSERT(memcmp(d, m_data, ItemRepositoryBucketSize + monsterBucketExtent * DataSize) == 0);
00439 Q_ASSERT(memcmp(m, m_objectMap, sizeof(short unsigned int) * m_objectMapSize) == 0);
00440 Q_ASSERT(memcmp(h, m_nextBucketHash, sizeof(short unsigned int) * NextBucketHashSize) == 0);
00441 Q_ASSERT(m_largestFreeItem == largestFree);
00442 Q_ASSERT(m_freeItemCount == freeItemCount);
00443 Q_ASSERT(m_dirty == dirty);
00444
00445 Q_ASSERT(static_cast<size_t>(file->pos()) == offset + DataSize + m_monsterBucketExtent * DataSize);
00446
00447 delete[] d;
00448 delete[] m;
00449 delete[] h;
00450 }
00451 #endif
00452 }
00453
00454 inline char* data() {
00455 return m_data;
00456 }
00457
00458 inline uint dataSize() const {
00459 return ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize;
00460 }
00461
00462
00463 unsigned short findIndex(const ItemRequest& request) const {
00464 m_lastUsed = 0;
00465
00466 unsigned short localHash = request.hash() % m_objectMapSize;
00467 unsigned short index = m_objectMap[localHash];
00468
00469 unsigned short follower = 0;
00470
00471 while(index && (follower = followerIndex(index)) && !(request.equals(itemFromIndex(index))))
00472 index = follower;
00473
00474 if(index && request.equals(itemFromIndex(index))) {
00475 return index;
00476 }
00477
00478 return 0;
00479 }
00480
00481
00482
00483 unsigned short index(const ItemRequest& request, unsigned int itemSize) {
00484 m_lastUsed = 0;
00485
00486 unsigned short localHash = request.hash() % m_objectMapSize;
00487 unsigned short index = m_objectMap[localHash];
00488 unsigned short insertedAt = 0;
00489
00490 unsigned short follower = 0;
00491
00492 while(index && (follower = followerIndex(index)) && !(request.equals(itemFromIndex(index))))
00493 index = follower;
00494
00495 if(index && request.equals(itemFromIndex(index)))
00496 return index;
00497
00498 ifDebugLostSpace( Q_ASSERT(!lostSpace()); )
00499
00500 prepareChange();
00501
00502 unsigned int totalSize = itemSize + AdditionalSpacePerItem;
00503
00504 if(m_monsterBucketExtent) {
00506 Q_ASSERT(totalSize > ItemRepositoryBucketSize);
00507 Q_ASSERT(m_available);
00508 m_available = 0;
00509
00510 insertedAt = AdditionalSpacePerItem;
00511 setFollowerIndex(insertedAt, 0);
00512 Q_ASSERT(m_objectMap[localHash] == 0);
00513 m_objectMap[localHash] = insertedAt;
00514
00515 if(markForReferenceCounting)
00516 enableDUChainReferenceCounting(m_data, dataSize());
00517
00518 request.createItem((Item*)(m_data + insertedAt));
00519
00520 if(markForReferenceCounting)
00521 disableDUChainReferenceCounting(m_data);
00522
00523 return insertedAt;
00524 }
00525
00526
00527 if(totalSize > m_available || (!itemSize && totalSize == m_available)) {
00528
00529 unsigned short currentIndex = m_largestFreeItem;
00530 unsigned short previousIndex = 0;
00531
00532 unsigned short freeChunkSize = 0;
00533
00535 while(currentIndex && freeSize(currentIndex) > itemSize) {
00536 unsigned short follower = followerIndex(currentIndex);
00537 if(follower && freeSize(follower) >= itemSize) {
00538
00539 previousIndex = currentIndex;
00540 currentIndex = follower;
00541 }else{
00542
00543 freeChunkSize = freeSize(currentIndex) - itemSize;
00544
00545
00546 if(freeChunkSize != 0 && freeChunkSize < AdditionalSpacePerItem+2) {
00547
00548
00549
00550
00551 freeChunkSize = freeSize(m_largestFreeItem) - itemSize;
00552
00553 if(freeChunkSize == 0 || freeChunkSize >= AdditionalSpacePerItem+2) {
00554 previousIndex = 0;
00555 currentIndex = m_largestFreeItem;
00556 }else{
00557 currentIndex = 0;
00558 }
00559 }
00560 break;
00561 }
00562 }
00563
00564 if(!currentIndex || freeSize(currentIndex) < (totalSize-AdditionalSpacePerItem))
00565 return 0;
00566
00567 if(previousIndex)
00568 setFollowerIndex(previousIndex, followerIndex(currentIndex));
00569 else
00570 m_largestFreeItem = followerIndex(currentIndex);
00571
00572 --m_freeItemCount;
00573
00574 ifDebugLostSpace( Q_ASSERT((uint)lostSpace() == (uint)(freeSize(currentIndex) + AdditionalSpacePerItem)); )
00575
00576 if(freeChunkSize) {
00577 Q_ASSERT(freeChunkSize >= AdditionalSpacePerItem+2);
00578 unsigned short freeItemSize = freeChunkSize - AdditionalSpacePerItem;
00579
00580 unsigned short freeItemPosition;
00581
00582 if(isBehindFreeSpace(currentIndex)) {
00583
00584 freeItemPosition = currentIndex;
00585 currentIndex += freeItemSize + AdditionalSpacePerItem;
00586 }else{
00587
00588 freeItemPosition = currentIndex + itemSize + AdditionalSpacePerItem;
00589 }
00590 setFreeSize(freeItemPosition, freeItemSize);
00591 insertFreeItem(freeItemPosition);
00592 }
00593
00594 insertedAt = currentIndex;
00595 Q_ASSERT((bool)m_freeItemCount == (bool)m_largestFreeItem);
00596 }else{
00597
00598 insertedAt = ItemRepositoryBucketSize - m_available;
00599
00600 insertedAt += AdditionalSpacePerItem;
00601
00602 m_available -= totalSize;
00603 }
00604
00605 ifDebugLostSpace( Q_ASSERT(lostSpace() == totalSize); )
00606
00607 Q_ASSERT(!index || !followerIndex(index));
00608
00609 Q_ASSERT(!m_objectMap[localHash] || index);
00610
00611 if(index)
00612 setFollowerIndex(index, insertedAt);
00613 setFollowerIndex(insertedAt, 0);
00614
00615 if(m_objectMap[localHash] == 0)
00616 m_objectMap[localHash] = insertedAt;
00617
00618
00619 #ifdef DEBUG_CREATEITEM_EXTENTS
00620 char* borderBehind = m_data + insertedAt + (totalSize-AdditionalSpacePerItem);
00621
00622 quint64 oldValueBehind = 0;
00623 if(m_available >= 8) {
00624 oldValueBehind = *(quint64*)borderBehind;
00625 *((quint64*)borderBehind) = 0xfafafafafafafafaLLU;
00626 }
00627 #endif
00628
00629
00630 if(markForReferenceCounting)
00631 enableDUChainReferenceCounting(m_data, dataSize());
00632
00633 request.createItem((Item*)(m_data + insertedAt));
00634
00635 if(markForReferenceCounting)
00636 disableDUChainReferenceCounting(m_data);
00637
00638 #ifdef DEBUG_CREATEITEM_EXTENTS
00639 if(m_available >= 8) {
00640
00641 Q_ASSERT(*((quint64*)borderBehind) == 0xfafafafafafafafaLLU);
00642 *((quint64*)borderBehind) = oldValueBehind;
00643 }
00644 #endif
00645
00646 Q_ASSERT(itemFromIndex(insertedAt)->hash() == request.hash());
00647 Q_ASSERT(itemFromIndex(insertedAt)->itemSize() == itemSize);
00648
00649 ifDebugLostSpace( if(lostSpace()) kDebug() << "lost space:" << lostSpace(); Q_ASSERT(!lostSpace()); )
00650
00651 return insertedAt;
00652 }
00653
00658
00659 bool hasClashingItem(uint hash, uint modulo) {
00660
00661 Q_ASSERT(modulo % ObjectMapSize == 0);
00662
00663 m_lastUsed = 0;
00664
00665 uint hashMod = hash % modulo;
00666 unsigned short localHash = hash % m_objectMapSize;
00667 unsigned short currentIndex = m_objectMap[localHash];
00668
00669 if(currentIndex == 0)
00670 return false;
00671
00672 while(currentIndex) {
00673 uint currentHash = itemFromIndex(currentIndex)->hash();
00674
00675 Q_ASSERT(currentHash % m_objectMapSize == localHash);
00676
00677 if(currentHash % modulo == hashMod)
00678 return true;
00679 currentIndex = followerIndex(currentIndex);
00680 }
00681 return false;
00682 }
00683
00684 struct ClashVisitor {
00685 ClashVisitor(int _hash, int _modulo) : result(0), hash(_hash), modulo(_modulo) {
00686 }
00687 bool operator()(const Item* item) {
00688 if((item->hash() % modulo) == (hash % modulo))
00689 result = item;
00690
00691 return true;
00692 }
00693 const Item* result;
00694 uint hash, modulo;
00695 };
00696
00697 void countFollowerIndexLengths(uint& usedSlots, uint& lengths, uint& slotCount, uint& longestInBucketFollowerChain) {
00698 for(uint a = 0; a < m_objectMapSize; ++a) {
00699 unsigned short currentIndex = m_objectMap[a];
00700 ++slotCount;
00701 uint length = 0;
00702
00703 if(currentIndex) {
00704 ++usedSlots;
00705
00706 while(currentIndex) {
00707 ++length;
00708 ++lengths;
00709 currentIndex = followerIndex(currentIndex);
00710 }
00711 if(length > longestInBucketFollowerChain) {
00712
00713
00714 longestInBucketFollowerChain = length;
00715 }
00716 }
00717 }
00718 }
00719
00720
00721
00722 bool hasClashingItemReal(uint hash, uint modulo) {
00723
00724 m_lastUsed = 0;
00725
00726 ClashVisitor visitor(hash, modulo);
00727 visitAllItems<ClashVisitor>(visitor);
00728 return (bool)visitor.result;
00729 }
00730
00731
00732 bool itemReachable(const Item* item, uint hash) const {
00733
00734 unsigned short localHash = hash % m_objectMapSize;
00735 unsigned short currentIndex = m_objectMap[localHash];
00736
00737 while(currentIndex) {
00738 if(itemFromIndex(currentIndex) == item)
00739 return true;
00740
00741 currentIndex = followerIndex(currentIndex);
00742 }
00743 return false;
00744 }
00745
00746 template<class Visitor>
00747 bool visitItemsWithHash(Visitor& visitor, uint hash, unsigned short bucketIndex) const {
00748 m_lastUsed = 0;
00749
00750 unsigned short localHash = hash % m_objectMapSize;
00751 unsigned short currentIndex = m_objectMap[localHash];
00752
00753 while(currentIndex) {
00754 if(!visitor(itemFromIndex(currentIndex), (bucketIndex << 16) + currentIndex))
00755 return false;
00756
00757 currentIndex = followerIndex(currentIndex);
00758 }
00759 return true;
00760 }
00761
00762
00763 template<class Repository>
00764 void deleteItem(unsigned short index, unsigned int hash, Repository& repository) {
00765 ifDebugLostSpace( Q_ASSERT(!lostSpace()); )
00766
00767 m_lastUsed = 0;
00768 prepareChange();
00769
00770 unsigned int size = itemFromIndex(index)->itemSize();
00771
00772 unsigned short localHash = hash % m_objectMapSize;
00773 unsigned short currentIndex = m_objectMap[localHash];
00774 unsigned short previousIndex = 0;
00775
00776
00777 while(currentIndex != index) {
00778 previousIndex = currentIndex;
00779 currentIndex = followerIndex(currentIndex);
00780
00781 Q_ASSERT(currentIndex);
00782 }
00783 Q_ASSERT(currentIndex == index);
00784
00785 if(!previousIndex)
00786
00787 m_objectMap[localHash] = followerIndex(index);
00788 else
00789 setFollowerIndex(previousIndex, followerIndex(index));
00790
00791 Item* item = const_cast<Item*>(itemFromIndex(index));
00792
00793 if(markForReferenceCounting)
00794 enableDUChainReferenceCounting(m_data, dataSize());
00795
00796 ItemRequest::destroy(item, repository);
00797
00798 if(markForReferenceCounting)
00799 disableDUChainReferenceCounting(m_data);
00800
00801 memset(item, 0, size);
00802
00803 if(m_monsterBucketExtent) {
00805 Q_ASSERT(!m_available);
00806 m_available = ItemRepositoryBucketSize;
00807
00808
00809 Q_ASSERT(currentIndex == AdditionalSpacePerItem);
00810 Q_ASSERT(m_objectMap[localHash] == 0);
00811 }else{
00813 setFreeSize(index, size);
00814
00815
00816 insertFreeItem(index);
00817
00818 if(m_freeItemCount == 1 && freeSize(m_largestFreeItem) + m_available == ItemRepositoryBucketSize) {
00819
00820
00821 m_available = ItemRepositoryBucketSize;
00822 m_freeItemCount = 0;
00823 m_largestFreeItem = 0;
00824 }
00825 }
00826
00827 Q_ASSERT((bool)m_freeItemCount == (bool)m_largestFreeItem);
00828 ifDebugLostSpace( Q_ASSERT(!lostSpace()); )
00829 #ifdef DEBUG_INCORRECT_DELETE
00830
00831 {
00832 unsigned short localHash = hash % m_objectMapSize;
00833 unsigned short currentIndex = m_objectMap[localHash];
00834
00835 while(currentIndex && currentIndex != index) {
00836 previousIndex = currentIndex;
00837 currentIndex = followerIndex(currentIndex);
00838 }
00839 Q_ASSERT(!currentIndex);
00840 }
00841 #endif
00842
00843 }
00844
00848 inline const Item* itemFromIndex(unsigned short index) const {
00849 m_lastUsed = 0;
00850 return (Item*)(m_data+index);
00851 }
00852
00853 bool isEmpty() const {
00854 return m_available == ItemRepositoryBucketSize;
00855 }
00856
00858 bool noNextBuckets() const {
00859 for(int a = 0; a < NextBucketHashSize; ++a)
00860 if(m_nextBucketHash[a])
00861 return false;
00862 return true;
00863 }
00864
00865 uint available() const {
00866 return m_available;
00867 }
00868
00869 uint usedMemory() const {
00870 return ItemRepositoryBucketSize - m_available;
00871 }
00872
00873 template<class Visitor>
00874 bool visitAllItems(Visitor& visitor) const {
00875 m_lastUsed = 0;
00876 for(uint a = 0; a < m_objectMapSize; ++a) {
00877 uint currentIndex = m_objectMap[a];
00878 while(currentIndex) {
00879
00880
00881
00882 if(!visitor((const Item*)(m_data+currentIndex)))
00883 return false;
00884
00885 currentIndex = followerIndex(currentIndex);
00886 }
00887 }
00888 return true;
00889 }
00890
00892 template<class Repository>
00893 int finalCleanup(Repository& repository) {
00894 int changed = 0;
00895
00896 while(m_dirty) {
00897 m_dirty = false;
00898
00899 for(uint a = 0; a < m_objectMapSize; ++a) {
00900 uint currentIndex = m_objectMap[a];
00901 while(currentIndex) {
00902
00903
00904
00905 const Item* item = (const Item*)(m_data+currentIndex);
00906
00907 if(!ItemRequest::persistent(item)) {
00908 changed += item->itemSize();
00909 deleteItem(currentIndex, item->hash(), repository);
00910 m_dirty = true;
00911 break;
00912 }
00913
00914 currentIndex = followerIndex(currentIndex);
00915 }
00916 }
00917 }
00918 return changed;
00919 }
00920
00921 unsigned short nextBucketForHash(uint hash) const {
00922 m_lastUsed = 0;
00923 return m_nextBucketHash[hash % NextBucketHashSize];
00924 }
00925
00926 void setNextBucketForHash(unsigned int hash, unsigned short bucket) {
00927 m_lastUsed = 0;
00928 prepareChange();
00929 m_nextBucketHash[hash % NextBucketHashSize] = bucket;
00930 }
00931
00932 uint freeItemCount() const {
00933 return m_freeItemCount;
00934 }
00935
00936 short unsigned int totalFreeItemsSize() const {
00937 short unsigned int ret = 0;
00938 short unsigned int currentIndex = m_largestFreeItem;
00939 while(currentIndex) {
00940 ret += freeSize(currentIndex);
00941 currentIndex = followerIndex(currentIndex);
00942 }
00943 return ret;
00944 }
00945
00946
00947 short unsigned int largestFreeSize() const {
00948 short unsigned int ret = 0;
00949 if(m_largestFreeItem)
00950 ret = freeSize(m_largestFreeItem);
00951 if(m_available > (uint)(AdditionalSpacePerItem + (uint)ret)) {
00952 ret = m_available - AdditionalSpacePerItem;
00953 Q_ASSERT(ret == (m_available - AdditionalSpacePerItem));
00954 }
00955 return ret;
00956 }
00957
00958 bool canAllocateItem(unsigned int size) const {
00959 short unsigned int currentIndex = m_largestFreeItem;
00960 while(currentIndex) {
00961 short unsigned int currentFree = freeSize(currentIndex);
00962 if(currentFree < size)
00963 return false;
00964
00965 if(size == currentFree || currentFree - size >= AdditionalSpacePerItem + 2)
00966 return true;
00967 currentIndex = followerIndex(currentIndex);
00968 }
00969
00970 return false;
00971 }
00972
00973 void tick() const {
00974 ++m_lastUsed;
00975 }
00976
00977
00978 int lastUsed() const {
00979 return m_lastUsed;
00980 }
00981
00982
00983 bool changed() const {
00984 return m_changed;
00985 }
00986
00987 void prepareChange() {
00988 m_changed = true;
00989 m_dirty = true;
00990 makeDataPrivate();
00991 }
00992
00993 bool dirty() const {
00994 return m_dirty;
00995 }
00996
00998 uint monsterBucketExtent() const {
00999 return m_monsterBucketExtent;
01000 }
01001
01002
01003 uint lostSpace() {
01004 if(m_monsterBucketExtent)
01005 return 0;
01006
01007 uint need = ItemRepositoryBucketSize - m_available;
01008 uint found = 0;
01009
01010 for(uint a = 0; a < m_objectMapSize; ++a) {
01011 uint currentIndex = m_objectMap[a];
01012 while(currentIndex) {
01013 found += ((const Item*)(m_data+currentIndex))->itemSize() + AdditionalSpacePerItem;
01014
01015 currentIndex = followerIndex(currentIndex);
01016 }
01017 }
01018 uint currentIndex = m_largestFreeItem;
01019 while(currentIndex) {
01020 found += freeSize(currentIndex) + AdditionalSpacePerItem;
01021
01022 currentIndex = followerIndex(currentIndex);
01023 }
01024 return need-found;
01025 }
01027 bool isFreeItem(unsigned short index) const {
01028 unsigned short currentIndex = m_largestFreeItem;
01029 unsigned short currentSize = 0xffff;
01030 while(currentIndex) {
01031 Q_ASSERT(freeSize(currentIndex) <= currentSize);
01032 currentSize = freeSize(currentIndex);
01033 if(index == currentIndex)
01034 return true;
01035
01036 currentIndex = followerIndex(currentIndex);
01037 }
01038 return false;
01039 }
01040
01041 private:
01042
01043 void makeDataPrivate() {
01044 if(m_mappedData == m_data) {
01045 short unsigned int* oldObjectMap = m_objectMap;
01046 short unsigned int* oldNextBucketHash = m_nextBucketHash;
01047
01048 m_data = new char[ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize];
01049 m_objectMap = new short unsigned int[m_objectMapSize];
01050 m_nextBucketHash = new short unsigned int[NextBucketHashSize];
01051
01052 memcpy(m_data, m_mappedData, ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize);
01053 memcpy(m_objectMap, oldObjectMap, m_objectMapSize * sizeof(short unsigned int));
01054 memcpy(m_nextBucketHash, oldNextBucketHash, NextBucketHashSize * sizeof(short unsigned int));
01055 }
01056 }
01057
01061 void insertFreeItem(unsigned short index) {
01062
01063
01064 if(!fixedItemSize) {
01065 unsigned short currentIndex = m_largestFreeItem;
01066 unsigned short previousIndex = 0;
01067
01068 while(currentIndex) {
01069 Q_ASSERT(currentIndex != index);
01070
01071 #ifndef QT_NO_DEBUG
01072 unsigned short currentFreeSize = freeSize(currentIndex);
01073 #endif
01074
01076
01077 if(currentIndex == index + freeSize(index) + AdditionalSpacePerItem) {
01078
01079
01080 if(previousIndex && followerIndex(currentIndex))
01081 Q_ASSERT(freeSize(previousIndex) >= freeSize(followerIndex(currentIndex)));
01082
01083 if(previousIndex)
01084 setFollowerIndex(previousIndex, followerIndex(currentIndex));
01085 else
01086 m_largestFreeItem = followerIndex(currentIndex);
01087
01088 --m_freeItemCount;
01089
01090
01091 setFreeSize(index, freeSize(index) + AdditionalSpacePerItem + freeSize(currentIndex));
01092
01093
01094 insertFreeItem(index);
01095 return;
01096 }
01097
01098
01099 if(index == currentIndex + freeSize(currentIndex) + AdditionalSpacePerItem) {
01100
01101 if(previousIndex && followerIndex(currentIndex))
01102 Q_ASSERT(freeSize(previousIndex) >= freeSize(followerIndex(currentIndex)));
01103
01104
01105
01106 if(previousIndex)
01107 setFollowerIndex(previousIndex, followerIndex(currentIndex));
01108 else
01109 m_largestFreeItem = followerIndex(currentIndex);
01110
01111 --m_freeItemCount;
01112
01113
01114 setFreeSize(currentIndex, freeSize(currentIndex) + AdditionalSpacePerItem + freeSize(index));
01115
01116
01117 insertFreeItem(currentIndex);
01118 return;
01119 }
01120
01121 previousIndex = currentIndex;
01122 currentIndex = followerIndex(currentIndex);
01123 if(currentIndex)
01124 Q_ASSERT(freeSize(currentIndex) <= currentFreeSize);
01125 }
01126 }
01127 insertToFreeChain(index);
01128 }
01129
01131 void insertToFreeChain(unsigned short index) {
01132
01133 if(!fixedItemSize) {
01135
01136 unsigned short currentIndex = m_largestFreeItem;
01137 unsigned short previousIndex = 0;
01138
01139 unsigned short size = freeSize(index);
01140
01141 while(currentIndex && freeSize(currentIndex) > size) {
01142 Q_ASSERT(currentIndex != index);
01143 previousIndex = currentIndex;
01144 currentIndex = followerIndex(currentIndex);
01145 }
01146
01147 if(currentIndex)
01148 Q_ASSERT(freeSize(currentIndex) <= size);
01149
01150 setFollowerIndex(index, currentIndex);
01151
01152 if(previousIndex) {
01153 Q_ASSERT(freeSize(previousIndex) >= size);
01154 setFollowerIndex(previousIndex, index);
01155 } else
01156
01157 m_largestFreeItem = index;
01158 }else{
01159 Q_ASSERT(freeSize(index) == fixedItemSize);
01160
01161 setFollowerIndex(index, m_largestFreeItem);
01162 m_largestFreeItem = index;
01163 }
01164
01165 ++m_freeItemCount;
01166 }
01167
01169 bool isBehindFreeSpace(unsigned short index) const {
01171 unsigned short currentIndex = m_largestFreeItem;
01172
01173 while(currentIndex) {
01174 if(index == currentIndex + freeSize(currentIndex) + AdditionalSpacePerItem)
01175 return true;
01176
01177 currentIndex = followerIndex(currentIndex);
01178 }
01179 return false;
01180 }
01181
01183 inline unsigned short followerIndex(unsigned short index) const {
01184 Q_ASSERT(index >= 2);
01185 return *((unsigned short*)(m_data+(index-2)));
01186 }
01187
01188 void setFollowerIndex(unsigned short index, unsigned short follower) {
01189 Q_ASSERT(index >= 2);
01190 *((unsigned short*)(m_data+(index-2))) = follower;
01191 }
01192
01193 inline unsigned short freeSize(unsigned short index) const {
01194 return *((unsigned short*)(m_data+index));
01195 }
01196
01197
01198 void setFreeSize(unsigned short index, unsigned short size) {
01199 *((unsigned short*)(m_data+index)) = size;
01200 }
01201
01202 uint m_monsterBucketExtent;
01203 unsigned int m_available;
01204 char* m_data;
01205 char* m_mappedData;
01206 short unsigned int* m_objectMap;
01207 uint m_objectMapSize;
01208 short unsigned int m_largestFreeItem;
01209 unsigned int m_freeItemCount;
01210
01211 unsigned short* m_nextBucketHash;
01212
01213 bool m_dirty;
01214 bool m_changed;
01215 mutable int m_lastUsed;
01216 };
01217
01218 template<bool lock>
01219 struct Locker {
01220 template<class T>
01221 Locker(const T& ) {
01222 }
01223 };
01224 template<>
01225 struct Locker<true> {
01226 Locker(QMutex* mutex) : m_mutex(mutex) {
01227 m_mutex->lock();
01228 }
01229 ~Locker() {
01230 m_mutex->unlock();
01231 }
01232 QMutex* m_mutex;
01233 };
01234
01240 template<class Item, bool markForReferenceCounting>
01241 class DynamicItem {
01242 public:
01243 DynamicItem(Item* i, void* start, uint size) : m_item(i), m_start(start) {
01244 if(markForReferenceCounting)
01245 enableDUChainReferenceCounting(m_start, size);
01246
01247 }
01248
01249 ~DynamicItem() {
01250 if(m_start) {
01251
01252 if(markForReferenceCounting)
01253 disableDUChainReferenceCounting(m_start);
01254 }
01255 }
01256
01257 DynamicItem(const DynamicItem& rhs) : m_item(rhs.m_item), m_start(rhs.m_start) {
01258
01259 Q_ASSERT(rhs.m_start);
01260 rhs.m_start = 0;
01261 }
01262
01263 Item* operator->() {
01264 return m_item;
01265 }
01266
01267 Item* m_item;
01268 private:
01269 mutable void* m_start;
01270 DynamicItem& operator=(const DynamicItem&);
01271 };
01272
01282 template<class Item, class ItemRequest, bool markForReferenceCounting = true, bool threadSafe = true, uint fixedItemSize = 0, unsigned int targetBucketHashSize = 524288*2>
01283 class ItemRepository : public AbstractItemRepository {
01284
01285 typedef Locker<threadSafe> ThisLocker;
01286
01287 typedef Bucket<Item, ItemRequest, markForReferenceCounting, fixedItemSize> MyBucket;
01288
01289 enum {
01290
01291
01292 bucketHashSize = (targetBucketHashSize / MyBucket::ObjectMapSize) * MyBucket::ObjectMapSize
01293 };
01294
01295 enum {
01296 BucketStartOffset = sizeof(uint) * 7 + sizeof(short unsigned int) * bucketHashSize
01297 };
01298
01299 public:
01303 ItemRepository(QString repositoryName, ItemRepositoryRegistry* registry = &globalItemRepositoryRegistry(), uint repositoryVersion = 1, AbstractRepositoryManager* manager = 0) : m_ownMutex(QMutex::Recursive), m_mutex(&m_ownMutex), m_repositoryName(repositoryName), m_registry(registry), m_file(0), m_dynamicFile(0), m_repositoryVersion(repositoryVersion), m_manager(manager) {
01304 m_unloadingEnabled = true;
01305 m_metaDataChanged = true;
01306 m_buckets.resize(10);
01307 m_buckets.fill(0);
01308 m_freeSpaceBucketsSize = 0;
01309 m_fastBuckets = m_buckets.data();
01310 m_bucketCount = m_buckets.size();
01311 m_bucketHashSize = bucketHashSize;
01312
01313 m_firstBucketForHash = new short unsigned int[bucketHashSize];
01314 memset(m_firstBucketForHash, 0, bucketHashSize * sizeof(short unsigned int));
01315
01316 m_statBucketHashClashes = m_statItemCount = 0;
01317 m_currentBucket = 1;
01318 if(m_registry)
01319 m_registry->registerRepository(this, m_manager);
01320 }
01321
01322 ~ItemRepository() {
01323 if(m_registry)
01324 m_registry->unRegisterRepository(this);
01325 close();
01326 }
01327
01330 void setUnloadingEnabled(bool enabled) {
01331 m_unloadingEnabled = enabled;
01332 }
01333
01337 unsigned int index(const ItemRequest& request) {
01338
01339 ThisLocker lock(m_mutex);
01340
01341 unsigned int hash = request.hash();
01342 unsigned int size = request.itemSize();
01343
01344 short unsigned int* bucketHashPosition = m_firstBucketForHash + (hash % bucketHashSize);
01345 short unsigned int previousBucketNumber = *bucketHashPosition;
01346 short unsigned int previousPreviousBucketNumber = 0;
01347
01348 uint useBucket = m_currentBucket;
01349 bool pickedBucketInChain = false;
01350 int reOrderFreeSpaceBucketIndex = -1;
01351
01352 while(previousBucketNumber) {
01353
01354 MyBucket* bucketPtr = m_fastBuckets[previousBucketNumber];
01355 if(!bucketPtr) {
01356 initializeBucket(previousBucketNumber);
01357 bucketPtr = m_fastBuckets[previousBucketNumber];
01358 }
01359
01360 unsigned short indexInBucket = bucketPtr->findIndex(request);
01361 if(indexInBucket) {
01362
01363 return (previousBucketNumber << 16) + indexInBucket;
01364 } else {
01365 #ifdef DEBUG_HASH_SEQUENCES
01366 if(previousBucketNumber==*bucketHashPosition)
01367 Q_ASSERT(bucketPtr->hasClashingItem(hash, bucketHashSize));
01368 else
01369 Q_ASSERT(bucketPtr->hasClashingItem(hash, MyBucket::NextBucketHashSize));
01370 #endif
01371
01372 if(!pickedBucketInChain && bucketPtr->canAllocateItem(size)) {
01373
01374
01375 useBucket = previousBucketNumber;
01376 reOrderFreeSpaceBucketIndex = m_freeSpaceBuckets.indexOf(useBucket);
01377 pickedBucketInChain = true;
01378 }
01379
01380
01381 short unsigned int next = bucketPtr->nextBucketForHash(hash);
01382 if(next) {
01383 previousPreviousBucketNumber = previousBucketNumber;
01384 previousBucketNumber = next;
01385 } else
01386 break;
01387 }
01388 }
01389
01390 m_metaDataChanged = true;
01391
01392 if(!pickedBucketInChain && useBucket == m_currentBucket) {
01393
01394 for(uint a = 0; a < m_freeSpaceBucketsSize; ++a) {
01395 MyBucket* bucketPtr = bucketForIndex(m_freeSpaceBuckets[a]);
01396 Q_ASSERT(bucketPtr->largestFreeSize());
01397
01398 if(bucketPtr->canAllocateItem(size)) {
01399
01400 useBucket = m_freeSpaceBuckets[a];
01401 reOrderFreeSpaceBucketIndex = a;
01402 break;
01403 }
01404 }
01405 }
01406
01407
01408 while(1) {
01409 if(useBucket >= m_bucketCount) {
01410 if(m_bucketCount >= 0xfffe) {
01411
01412 kWarning() << "Found no room for an item in" << m_repositoryName << "size of the item:" << request.itemSize();
01413 return 0;
01414 }else{
01415
01416 uint oldBucketCount = m_bucketCount;
01417 m_bucketCount += 10;
01418 m_buckets.resize(m_bucketCount);
01419 m_fastBuckets = m_buckets.data();
01420 memset(m_fastBuckets + oldBucketCount, 0, (m_bucketCount-oldBucketCount) * sizeof(void*));
01421 }
01422 }
01423 MyBucket* bucketPtr = m_fastBuckets[useBucket];
01424 if(!bucketPtr) {
01425 initializeBucket(useBucket);
01426 bucketPtr = m_fastBuckets[useBucket];
01427 }
01428
01429 ENSURE_REACHABLE(useBucket);
01430 Q_ASSERT(!bucketPtr->findIndex(request));
01431
01432 unsigned short indexInBucket = bucketPtr->index(request, size);
01433
01434
01435
01436 if(bucketPtr->isEmpty() && !indexInBucket) {
01438 uint totalSize = size + MyBucket::AdditionalSpacePerItem;
01439
01440 Q_ASSERT((totalSize > ItemRepositoryBucketSize));
01441
01442 useBucket = 0;
01443
01445 int rangeStart = -1;
01446 int rangeEnd = -1;
01447 for(uint a = 0; a < m_freeSpaceBucketsSize; ++a) {
01448 MyBucket* bucketPtr = bucketForIndex(m_freeSpaceBuckets[a]);
01449 if(bucketPtr->isEmpty()) {
01450
01451 int index = (int)m_freeSpaceBuckets[a];
01452 if(rangeEnd != index) {
01453 rangeStart = index;
01454 rangeEnd = index+1;
01455 }else{
01456 ++rangeEnd;
01457 }
01458 if(rangeStart != rangeEnd) {
01459 uint extent = rangeEnd - rangeStart - 1;
01460 uint totalAvailableSpace = bucketForIndex(rangeStart)->available() + MyBucket::DataSize * (rangeEnd - rangeStart - 1);
01461 if(totalAvailableSpace > totalSize) {
01462 Q_ASSERT(extent);
01464 Q_ASSERT((uint)m_freeSpaceBuckets[a-extent] == (uint)rangeStart);
01465 m_freeSpaceBuckets.remove(a-extent, extent+1);
01466 m_freeSpaceBucketsSize = m_freeSpaceBuckets.size();
01467 useBucket = rangeStart;
01468 convertMonsterBucket(rangeStart, extent);
01469
01470 break;
01471 }
01472 }
01473 }
01474 }
01475 if(!useBucket) {
01476
01477 uint needMonsterExtent = (totalSize - ItemRepositoryBucketSize) / MyBucket::DataSize + 1;
01478 Q_ASSERT(needMonsterExtent);
01479 if(m_currentBucket + needMonsterExtent + 1 > (uint)m_buckets.size()) {
01480 uint oldBucketCount = m_bucketCount;
01481 m_bucketCount += 10 + needMonsterExtent + 1;
01482 m_buckets.resize(m_bucketCount);
01483 m_fastBuckets = m_buckets.data();
01484
01485 memset(m_fastBuckets + oldBucketCount, 0, (m_bucketCount-oldBucketCount) * sizeof(void*));
01486 }
01487 useBucket = m_currentBucket;
01488
01489 convertMonsterBucket(useBucket, needMonsterExtent);
01490 m_currentBucket += 1 + needMonsterExtent;
01491 Q_ASSERT(m_fastBuckets[m_currentBucket - 1 - needMonsterExtent] && m_fastBuckets[m_currentBucket - 1 - needMonsterExtent]->monsterBucketExtent() == needMonsterExtent);
01492 }
01493 Q_ASSERT(useBucket);
01494 bucketPtr = bucketForIndex(useBucket);
01495
01496 indexInBucket = bucketPtr->index(request, size);
01497 Q_ASSERT(indexInBucket);
01498 }
01499
01500 if(indexInBucket) {
01501 ++m_statItemCount;
01502
01503 if(!(*bucketHashPosition)) {
01504 Q_ASSERT(!previousBucketNumber);
01505 (*bucketHashPosition) = useBucket;
01506 ENSURE_REACHABLE(useBucket);
01507 } else if(!pickedBucketInChain && previousBucketNumber && previousBucketNumber != useBucket) {
01508
01509 ++m_statBucketHashClashes;
01510
01512 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, previousBucketNumber));)
01513
01514
01515
01516 QPair<unsigned int, unsigned int> intersect = hashChainIntersection(*bucketHashPosition, useBucket, hash);
01517
01518 Q_ASSERT(m_buckets[previousBucketNumber]->nextBucketForHash(hash) == 0);
01519
01520 if(!intersect.second) {
01521 ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(*bucketHashPosition, hash, useBucket));)
01522 ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(useBucket, hash, previousBucketNumber));)
01523
01524 Q_ASSERT(m_buckets[previousBucketNumber]->nextBucketForHash(hash) == 0);
01525
01526 m_buckets[previousBucketNumber]->setNextBucketForHash(hash, useBucket);
01527 ENSURE_REACHABLE(useBucket);
01528 ENSURE_REACHABLE(previousBucketNumber);
01529
01530 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, useBucket));)
01531 } else if(intersect.first) {
01532 ifDebugInfiniteRecursion(Q_ASSERT(bucketForIndex(intersect.first)->nextBucketForHash(hash) == intersect.second);)
01533 ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(*bucketHashPosition, hash, useBucket));)
01534 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, intersect.second));)
01535 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, intersect.first));)
01536 ifDebugInfiniteRecursion(Q_ASSERT(bucketForIndex(intersect.first)->nextBucketForHash(hash) == intersect.second);)
01537
01538 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(useBucket, hash, intersect.second));)
01539 ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(useBucket, hash, intersect.first));)
01540
01541 bucketForIndex(intersect.first)->setNextBucketForHash(hash, useBucket);
01542
01543 ENSURE_REACHABLE(useBucket);
01544 ENSURE_REACHABLE(intersect.second);
01545
01546 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, useBucket));)
01547 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, intersect.second));)
01548 } else {
01549
01550
01551
01552
01553 ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(*bucketHashPosition, hash, useBucket));)
01554 ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(useBucket, hash, *bucketHashPosition));)
01555 unsigned short oldStart = *bucketHashPosition;
01556
01557 *bucketHashPosition = useBucket;
01558
01559 ENSURE_REACHABLE(useBucket);
01560 ENSURE_REACHABLE(oldStart);
01561 Q_UNUSED(oldStart);
01562 }
01563 }
01564
01565 if(reOrderFreeSpaceBucketIndex != -1)
01566 updateFreeSpaceOrder(reOrderFreeSpaceBucketIndex);
01567
01568 return (useBucket << 16) + indexInBucket;
01569 }else{
01570
01571 Q_ASSERT(!pickedBucketInChain);
01572 Q_ASSERT(reOrderFreeSpaceBucketIndex == -1);
01573 Q_ASSERT(useBucket == m_currentBucket);
01574
01575 if(!bucketForIndex(useBucket)->isEmpty())
01576 putIntoFreeList(useBucket, bucketPtr);
01577
01578 ++m_currentBucket;
01579 useBucket = m_currentBucket;
01580 }
01581 }
01582
01583
01584 kWarning() << "Found no bucket for an item in" << m_repositoryName;
01585 return 0;
01586 }
01587
01589 unsigned int findIndex(const ItemRequest& request) {
01590
01591 ThisLocker lock(m_mutex);
01592
01593 unsigned int hash = request.hash();
01594
01595 short unsigned int* bucketHashPosition = m_firstBucketForHash + (hash % bucketHashSize);
01596 short unsigned int previousBucketNumber = *bucketHashPosition;
01597
01598 while(previousBucketNumber) {
01599
01600
01601 MyBucket* bucketPtr = m_fastBuckets[previousBucketNumber];
01602 if(!bucketPtr) {
01603 initializeBucket(previousBucketNumber);
01604 bucketPtr = m_fastBuckets[previousBucketNumber];
01605 }
01606
01607 unsigned short indexInBucket = bucketPtr->findIndex(request);
01608 if(indexInBucket) {
01609
01610 return (previousBucketNumber << 16) + indexInBucket;
01611 } else {
01612
01613
01614 short unsigned int next = bucketPtr->nextBucketForHash(hash);
01615 if(next)
01616 previousBucketNumber = next;
01617 else
01618 break;
01619 }
01620 }
01621
01622 return 0;
01623 }
01624
01626 void deleteItem(unsigned int index) {
01627 ThisLocker lock(m_mutex);
01628
01629 m_metaDataChanged = true;
01630
01631 unsigned short bucket = (index >> 16);
01632 Q_ASSERT(bucket);
01633
01634 unsigned int hash = itemFromIndex(index)->hash();
01635
01636 short unsigned int* bucketHashPosition = m_firstBucketForHash + (hash % bucketHashSize);
01637 short unsigned int previousBucketNumber = *bucketHashPosition;
01638
01639 Q_ASSERT(previousBucketNumber);
01640
01641 if(previousBucketNumber == bucket)
01642 previousBucketNumber = 0;
01643
01644 MyBucket* previousBucketPtr = 0;
01645
01646
01647
01648 while(previousBucketNumber) {
01649
01650
01651 previousBucketPtr = m_fastBuckets[previousBucketNumber];
01652 if(!previousBucketPtr) {
01653 initializeBucket(previousBucketNumber);
01654 previousBucketPtr = m_fastBuckets[previousBucketNumber];
01655 }
01656
01657 short unsigned int nextBucket = previousBucketPtr->nextBucketForHash(hash);
01658 Q_ASSERT(nextBucket);
01659 if(nextBucket == bucket)
01660 break;
01661 else
01662 previousBucketNumber = nextBucket;
01663 }
01664
01665
01666 Q_ASSERT(previousBucketNumber || *bucketHashPosition == bucket);
01667
01668 MyBucket* bucketPtr = m_fastBuckets[bucket];
01669 if(!bucketPtr) {
01670 initializeBucket(bucket);
01671 bucketPtr = m_fastBuckets[bucket];
01672 }
01673
01674 --m_statItemCount;
01675
01676 bucketPtr->deleteItem(index, hash, *this);
01677
01681
01682
01683 if(previousBucketNumber == 0) {
01684
01685
01686 Q_ASSERT(*bucketHashPosition == bucket);
01687 unsigned short previous = bucket;
01688 while(!bucketPtr->hasClashingItem(hash, bucketHashSize))
01689 {
01690
01691 unsigned short next = bucketPtr->nextBucketForHash(hash);
01692 ENSURE_REACHABLE(next);
01693 ENSURE_REACHABLE(previous);
01694
01695 *bucketHashPosition = next;
01696
01697 ENSURE_REACHABLE(previous);
01698 ENSURE_REACHABLE(next);
01699
01700 previous = next;
01701
01702 if(next) {
01703 bucketPtr = m_fastBuckets[next];
01704
01705 if(!bucketPtr)
01706 {
01707 initializeBucket(next);
01708 bucketPtr = m_fastBuckets[next];
01709 }
01710 }else{
01711 break;
01712 }
01713 }
01714 }else{
01715 if(!bucketPtr->hasClashingItem(hash, MyBucket::NextBucketHashSize)) {
01716
01718 walkBucketLinks(*bucketHashPosition, hash);
01719
01720 Q_ASSERT(previousBucketPtr->nextBucketForHash(hash) == bucket);
01721
01722 ENSURE_REACHABLE(bucket);
01723
01724 previousBucketPtr->setNextBucketForHash(hash, bucketPtr->nextBucketForHash(hash));
01725
01726 ENSURE_REACHABLE(bucket);
01727
01729 Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, bucketPtr->nextBucketForHash(hash)));
01730
01731 Q_ASSERT(bucketPtr->nextBucketForHash(hash) != previousBucketNumber);
01732 }
01733 }
01734
01735 ENSURE_REACHABLE(bucket);
01736
01737 if(bucketPtr->monsterBucketExtent()) {
01738
01739 uint newBuckets = bucketPtr->monsterBucketExtent()+1;
01740 Q_ASSERT(bucketPtr->isEmpty());
01741 convertMonsterBucket(bucket, 0);
01742 for(uint created = bucket; created < bucket + newBuckets; ++created) {
01743 putIntoFreeList(created, bucketForIndex(created));
01744 #ifdef DEBUG_MONSTERBUCKETS
01745 Q_ASSERT(m_freeSpaceBuckets.indexOf(created) != -1);
01746 #endif
01747 }
01748 }else{
01749 putIntoFreeList(bucket, bucketPtr);
01750 }
01751
01752 #ifdef DEBUG_HASH_SEQUENCES
01753 Q_ASSERT(*bucketHashPosition == 0 || bucketForIndex(*bucketHashPosition)->hasClashingItem(hash, bucketHashSize));
01754 #endif
01755 }
01756
01762
01763 typedef DynamicItem<Item, markForReferenceCounting> MyDynamicItem;
01764
01765 MyDynamicItem dynamicItemFromIndex(unsigned int index) {
01766 Q_ASSERT(index);
01767
01768 ThisLocker lock(m_mutex);
01769
01770 unsigned short bucket = (index >> 16);
01771 Q_ASSERT(bucket);
01772
01773 MyBucket* bucketPtr = m_fastBuckets[bucket];
01774 Q_ASSERT(bucket < m_bucketCount);
01775 if(!bucketPtr) {
01776 initializeBucket(bucket);
01777 bucketPtr = m_fastBuckets[bucket];
01778 }
01779 bucketPtr->prepareChange();
01780 unsigned short indexInBucket = index & 0xffff;
01781 return MyDynamicItem(const_cast<Item*>(bucketPtr->itemFromIndex(indexInBucket)), bucketPtr->data(), bucketPtr->dataSize());
01782 }
01783
01791 Item* dynamicItemFromIndexSimple(unsigned int index) {
01792 Q_ASSERT(index);
01793
01794 ThisLocker lock(m_mutex);
01795
01796 unsigned short bucket = (index >> 16);
01797 Q_ASSERT(bucket);
01798
01799 MyBucket* bucketPtr = m_fastBuckets[bucket];
01800 Q_ASSERT(bucket < m_bucketCount);
01801 if(!bucketPtr) {
01802 initializeBucket(bucket);
01803 bucketPtr = m_fastBuckets[bucket];
01804 }
01805 bucketPtr->prepareChange();
01806 unsigned short indexInBucket = index & 0xffff;
01807 return const_cast<Item*>(bucketPtr->itemFromIndex(indexInBucket));
01808 }
01812 template<class Action>
01813 void dynamicAction(unsigned int index, Action& action) {
01814 Q_ASSERT(index);
01815
01816 ThisLocker lock(m_mutex);
01817
01818 unsigned short bucket = (index >> 16);
01819 Q_ASSERT(bucket);
01820
01821 MyBucket* bucketPtr = m_fastBuckets[bucket];
01822 Q_ASSERT(bucket < m_bucketCount);
01823 if(!bucketPtr) {
01824 initializeBucket(bucket);
01825 bucketPtr = m_fastBuckets[bucket];
01826 }
01827 bucketPtr->prepareChange();
01828 unsigned short indexInBucket = index & 0xffff;
01829
01830 if(markForReferenceCounting)
01831 enableDUChainReferenceCounting(bucketPtr->data(), bucketPtr->dataSize());
01832
01833 action(const_cast<Item&>(*bucketPtr->itemFromIndex(indexInBucket, 0)));
01834
01835 if(markForReferenceCounting)
01836 disableDUChainReferenceCounting(bucketPtr->data());
01837 }
01838
01841 const Item* itemFromIndex(unsigned int index) const {
01842 Q_ASSERT(index);
01843
01844 ThisLocker lock(m_mutex);
01845
01846 unsigned short bucket = (index >> 16);
01847 Q_ASSERT(bucket);
01848
01849 const MyBucket* bucketPtr = m_fastBuckets[bucket];
01850 Q_ASSERT(bucket < m_bucketCount);
01851 if(!bucketPtr) {
01852 initializeBucket(bucket);
01853 bucketPtr = m_fastBuckets[bucket];
01854 }
01855 unsigned short indexInBucket = index & 0xffff;
01856 return bucketPtr->itemFromIndex(indexInBucket);
01857 }
01858
01859 struct Statistics {
01860 Statistics() : loadedBuckets(-1), currentBucket(-1), usedMemory(-1), loadedMonsterBuckets(-1), usedSpaceForBuckets(-1),
01861 freeSpaceInBuckets(-1), lostSpace(-1), freeUnreachableSpace(-1), hashClashedItems(-1), totalItems(-1), hashSize(-1), hashUse(-1),
01862 averageInBucketHashSize(-1), averageInBucketUsedSlotCount(-1), averageInBucketSlotChainLength(-1), longestInBucketChain(-1), longestNextBucketChain(-1), totalBucketFollowerSlots(-1), averageNextBucketForHashSequenceLength(-1) {
01863 }
01864
01865 uint loadedBuckets;
01866 uint currentBucket;
01867 uint usedMemory;
01868 uint loadedMonsterBuckets;
01869 uint usedSpaceForBuckets;
01870 uint freeSpaceInBuckets;
01871 uint lostSpace;
01872 uint freeUnreachableSpace;
01873 uint hashClashedItems;
01874 uint totalItems;
01875 uint emptyBuckets;
01876 uint hashSize;
01877 uint hashUse;
01878 uint averageInBucketHashSize;
01879 uint averageInBucketUsedSlotCount;
01880 float averageInBucketSlotChainLength;
01881 uint longestInBucketChain;
01882
01883 uint longestNextBucketChain;
01884 uint totalBucketFollowerSlots;
01885 float averageNextBucketForHashSequenceLength;
01886
01887 QString print() const {
01888 QString ret;
01889 ret += QString("loaded buckets: %1 current bucket: %2 used memory: %3 loaded monster buckets: %4").arg(loadedBuckets).arg(currentBucket).arg(usedMemory).arg(loadedMonsterBuckets);
01890 ret += QString("\nbucket hash clashed items: %1 total items: %2").arg(hashClashedItems).arg(totalItems);
01891 ret += QString("\nused space for buckets: %1 free space in buckets: %2 lost space: %3").arg(usedSpaceForBuckets).arg(freeSpaceInBuckets).arg(lostSpace);
01892 ret += QString("\nfree unreachable space: %1 empty buckets: %2").arg(freeUnreachableSpace).arg(emptyBuckets);
01893 ret += QString("\nhash size: %1 hash slots used: %2").arg(hashSize).arg(hashUse);
01894 ret += QString("\naverage in-bucket hash size: %1 average in-bucket used hash slot count: %2 average in-bucket slot chain length: %3 longest in-bucket follower chain: %4").arg(averageInBucketHashSize).arg(averageInBucketUsedSlotCount).arg(averageInBucketSlotChainLength).arg(longestInBucketChain);
01895 ret += QString("\ntotal count of used next-bucket-for-hash slots: %1 average next-bucket-for-hash sequence length: %2 longest next-bucket chain: %3").arg(totalBucketFollowerSlots).arg(averageNextBucketForHashSequenceLength).arg(longestNextBucketChain);
01896 return ret;
01897 }
01898 operator QString() const {
01899 return print();
01900 }
01901 };
01902
01903 QString printStatistics() const {
01904 return statistics().print();
01905 }
01906
01907 Statistics statistics() const {
01908 Statistics ret;
01909 uint loadedBuckets = 0;
01910 for(uint a = 0; a < m_bucketCount; ++a)
01911 if(m_fastBuckets[a])
01912 ++loadedBuckets;
01913
01914 #ifdef DEBUG_MONSTERBUCKETS
01915 for(int a = 0; a < m_freeSpaceBucketsSize; ++a) {
01916 if(a > 0) {
01917 uint prev = a-1;
01918 uint prevLargestFree = bucketForIndex(m_freeSpaceBuckets[prev])->largestFreeSize();
01919 uint largestFree = bucketForIndex(m_freeSpaceBuckets[a])->largestFreeSize();
01920 Q_ASSERT( (prevLargestFree < largestFree) || (prevLargestFree == largestFree &&
01921 m_freeSpaceBuckets[prev] < m_freeSpaceBuckets[a]) );
01922 }
01923 }
01924 #endif
01925 ret.hashSize = bucketHashSize;
01926 ret.hashUse = 0;
01927 for(uint a = 0; a < m_bucketHashSize; ++a)
01928 if(m_firstBucketForHash[a])
01929 ++ret.hashUse;
01930
01931 ret.emptyBuckets = 0;
01932
01933 uint loadedMonsterBuckets = 0;
01934 for(uint a = 0; a < m_bucketCount; ++a)
01935 if(m_fastBuckets[a] && m_fastBuckets[a]->monsterBucketExtent())
01936 loadedMonsterBuckets += m_fastBuckets[a]->monsterBucketExtent()+1;
01937
01938 uint usedBucketSpace = MyBucket::DataSize * m_currentBucket;
01939 uint freeBucketSpace = 0, freeUnreachableSpace = 0;
01940 uint lostSpace = 0;
01941 uint totalInBucketHashSize = 0, totalInBucketUsedSlotCount = 0, totalInBucketChainLengths = 0;
01942 uint bucketCount = 0;
01943
01944 ret.totalBucketFollowerSlots = 0;
01945 ret.averageNextBucketForHashSequenceLength = 0;
01946 ret.longestNextBucketChain = 0;
01947 ret.longestInBucketChain = 0;
01948
01949 for(uint a = 1; a < m_currentBucket+1; ++a) {
01950 MyBucket* bucket = bucketForIndex(a);
01951 if(bucket) {
01952 ++bucketCount;
01953
01954 bucket->countFollowerIndexLengths(totalInBucketUsedSlotCount, totalInBucketChainLengths, totalInBucketHashSize, ret.longestInBucketChain);
01955
01956 for(uint aa = 0; aa < MyBucket::NextBucketHashSize; ++aa) {
01957 uint length = 0;
01958 uint next = bucket->nextBucketForHash(aa);
01959 if(next) {
01960 ++ret.totalBucketFollowerSlots;
01961 while(next) {
01962 ++length;
01963 ++ret.averageNextBucketForHashSequenceLength;
01964 next = bucketForIndex(next)->nextBucketForHash(aa);
01965 }
01966 }
01967 if(length > ret.longestNextBucketChain) {
01968
01969 ret.longestNextBucketChain = length;
01970 }
01971 }
01972
01973 uint bucketFreeSpace = bucket->totalFreeItemsSize() + bucket->available();
01974 freeBucketSpace += bucketFreeSpace;
01975 if(m_freeSpaceBuckets.indexOf(a) == -1)
01976 freeUnreachableSpace += bucketFreeSpace;
01977
01978 if(bucket->isEmpty()) {
01979 ++ret.emptyBuckets;
01980 Q_ASSERT(!bucket->monsterBucketExtent());
01981 #ifdef DEBUG_MONSTERBUCKETS
01982 Q_ASSERT(m_freeSpaceBuckets.contains(a));
01983 #endif
01984 }
01985
01986 lostSpace += bucket->lostSpace();
01987 a += bucket->monsterBucketExtent();
01988 }
01989 }
01990
01991 if(ret.totalBucketFollowerSlots)
01992 ret.averageNextBucketForHashSequenceLength /= ret.totalBucketFollowerSlots;
01993
01994 ret.loadedBuckets = loadedBuckets;
01995 ret.currentBucket = m_currentBucket;
01996 ret.usedMemory = usedMemory();
01997 ret.loadedMonsterBuckets = loadedMonsterBuckets;
01998
01999 ret.hashClashedItems = m_statBucketHashClashes;
02000 ret.totalItems = m_statItemCount;
02001 ret.usedSpaceForBuckets = usedBucketSpace;
02002 ret.freeSpaceInBuckets = freeBucketSpace;
02003 ret.lostSpace = lostSpace;
02004
02005 ret.freeUnreachableSpace = freeUnreachableSpace;
02006 ret.averageInBucketHashSize = totalInBucketHashSize / bucketCount;
02007 ret.averageInBucketUsedSlotCount = totalInBucketUsedSlotCount / bucketCount;
02008 ret.averageInBucketSlotChainLength = float(totalInBucketChainLengths) / totalInBucketUsedSlotCount;
02009
02010
02011 return ret;
02012 }
02013
02014 uint usedMemory() const {
02015 uint used = 0;
02016 for(int a = 0; a < m_buckets.size(); ++a) {
02017 if(m_buckets[a]) {
02018 used += m_buckets[a]->usedMemory();
02019 }
02020 }
02021 return used;
02022 }
02023
02028 template<class Visitor>
02029 void visitAllItems(Visitor& visitor, bool onlyInMemory = false) const {
02030 ThisLocker lock(m_mutex);
02031 for(uint a = 1; a <= m_currentBucket; ++a) {
02032 if(!onlyInMemory || m_buckets[a]) {
02033 if(bucketForIndex(a) && !bucketForIndex(a)->visitAllItems(visitor))
02034 return;
02035 }
02036 }
02037 }
02038
02042 template<class Visitor>
02043 void visitItemsWithHash(Visitor& visitor, uint hash) const {
02044 ThisLocker lock(m_mutex);
02045
02046 short unsigned int bucket = *(m_firstBucketForHash + (hash % bucketHashSize));
02047
02048 while(bucket) {
02049
02050 MyBucket* bucketPtr = m_fastBuckets[bucket];
02051 if(!bucketPtr) {
02052 initializeBucket(bucket);
02053 bucketPtr = m_fastBuckets[bucket];
02054 }
02055
02056 if(!bucketPtr->visitItemsWithHash(visitor, hash, bucket))
02057 return;
02058
02059 bucket = bucketPtr->nextBucketForHash(hash);
02060 }
02061 }
02062
02065 virtual void store() {
02066 QMutexLocker lock(m_mutex);
02067 if(m_file) {
02068
02069 if(!m_file->open( QFile::ReadWrite ) || !m_dynamicFile->open( QFile::ReadWrite )) {
02070 kFatal() << "cannot re-open repository file for storing";
02071 return;
02072 }
02073
02074 for(uint a = 0; a < m_bucketCount; ++a) {
02075 if(m_fastBuckets[a]) {
02076 if(m_fastBuckets[a]->changed()) {
02077 storeBucket(a);
02078 }
02079 if(m_unloadingEnabled) {
02080 const int unloadAfterTicks = 2;
02081 if(m_fastBuckets[a]->lastUsed() > unloadAfterTicks) {
02082 delete m_fastBuckets[a];
02083 m_fastBuckets[a] = 0;
02084 }else{
02085 m_fastBuckets[a]->tick();
02086 }
02087 }
02088 }
02089 }
02090
02091 if(m_metaDataChanged) {
02092 Q_ASSERT(m_dynamicFile);
02093
02094 m_file->seek(0);
02095 m_file->write((char*)&m_repositoryVersion, sizeof(uint));
02096 uint hashSize = bucketHashSize;
02097 m_file->write((char*)&hashSize, sizeof(uint));
02098 uint itemRepositoryVersion = staticItemRepositoryVersion();
02099 m_file->write((char*)&itemRepositoryVersion, sizeof(uint));
02100 m_file->write((char*)&m_statBucketHashClashes, sizeof(uint));
02101 m_file->write((char*)&m_statItemCount, sizeof(uint));
02102
02103 uint bucketCount = m_buckets.size();
02104 m_file->write((char*)&bucketCount, sizeof(uint));
02105 m_file->write((char*)&m_currentBucket, sizeof(uint));
02106 m_file->write((char*)m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize);
02107 Q_ASSERT(m_file->pos() == BucketStartOffset);
02108
02109 Q_ASSERT(m_freeSpaceBucketsSize == (uint)m_freeSpaceBuckets.size());
02110 m_dynamicFile->seek(0);
02111 m_dynamicFile->write((char*)&m_freeSpaceBucketsSize, sizeof(uint));
02112 m_dynamicFile->write((char*)m_freeSpaceBuckets.data(), sizeof(uint) * m_freeSpaceBucketsSize);
02113
02114
02115
02116
02117
02118
02119
02120
02121
02122
02123
02124
02125
02126
02127
02128
02129
02130
02131
02132
02133
02134
02135
02136
02137
02138
02139
02140
02141
02142
02143
02144 }
02145
02146 m_file->close();
02147 m_dynamicFile->close();
02148 Q_ASSERT(!m_file->isOpen());
02149 Q_ASSERT(!m_dynamicFile->isOpen());
02150 }
02151 }
02152
02159 QMutex* mutex() const {
02160 return m_mutex;
02161 }
02162
02164 void setMutex(QMutex* mutex) {
02165 m_mutex = mutex;
02166 }
02167
02168 virtual QString repositoryName() const {
02169 return m_repositoryName;
02170 }
02171
02172 private:
02173
02176 void updateFreeSpaceOrder(uint index) {
02177 m_metaDataChanged = true;
02178
02179 unsigned int* freeSpaceBuckets = m_freeSpaceBuckets.data();
02180
02181 Q_ASSERT(index < (uint)m_freeSpaceBucketsSize);
02182 MyBucket* bucketPtr = bucketForIndex(freeSpaceBuckets[index]);
02183
02184 unsigned short largestFreeSize = bucketPtr->largestFreeSize();
02185
02186 if(largestFreeSize == 0 || (bucketPtr->freeItemCount() <= MyBucket::MaxFreeItemsForHide && largestFreeSize <= MyBucket::MaxFreeSizeForHide)) {
02187
02188 m_freeSpaceBuckets.remove(index);
02189 m_freeSpaceBucketsSize = m_freeSpaceBuckets.size();
02190 }else{
02191
02192 while(1) {
02193 int prev = index-1;
02194 int next = index+1;
02195 if(prev >= 0 && (bucketForIndex(freeSpaceBuckets[prev])->largestFreeSize() > largestFreeSize ||
02196 (bucketForIndex(freeSpaceBuckets[prev])->largestFreeSize() == largestFreeSize && freeSpaceBuckets[index] < freeSpaceBuckets[prev]))
02197 ) {
02198
02199 uint oldPrevValue = freeSpaceBuckets[prev];
02200 freeSpaceBuckets[prev] = freeSpaceBuckets[index];
02201 freeSpaceBuckets[index] = oldPrevValue;
02202 index = prev;
02203 }else if(next < (int)m_freeSpaceBucketsSize && (bucketForIndex(freeSpaceBuckets[next])->largestFreeSize() < largestFreeSize ||
02204 (bucketForIndex(freeSpaceBuckets[next])->largestFreeSize() == largestFreeSize && freeSpaceBuckets[index] > freeSpaceBuckets[next]))) {
02205
02206 uint oldNextValue = freeSpaceBuckets[next];
02207 freeSpaceBuckets[next] = freeSpaceBuckets[index];
02208 freeSpaceBuckets[index] = oldNextValue;
02209 index = next;
02210 }else {
02211 break;
02212 }
02213 }
02214 }
02215 }
02216
02225 MyBucket* convertMonsterBucket(short unsigned int bucketNumber, unsigned int extent) {
02226 Q_ASSERT(bucketNumber);
02227 MyBucket* bucketPtr = m_fastBuckets[bucketNumber];
02228 if(!bucketPtr) {
02229 initializeBucket(bucketNumber);
02230 bucketPtr = m_fastBuckets[bucketNumber];
02231 }
02232
02233 if(extent) {
02234
02235 #ifdef DEBUG_MONSTERBUCKETS
02236 for(uint index = bucketNumber; index < bucketNumber + 1 + extent; ++index) {
02237 Q_ASSERT(bucketPtr->isEmpty());
02238 Q_ASSERT(!bucketPtr->monsterBucketExtent());
02239 Q_ASSERT(m_freeSpaceBuckets.indexOf(index) == -1);
02240 }
02241 #endif
02242 for(uint index = bucketNumber; index < bucketNumber + 1 + extent; ++index)
02243 deleteBucket(index);
02244
02245 m_fastBuckets[bucketNumber] = new MyBucket();
02246
02247 m_fastBuckets[bucketNumber]->initialize(extent);
02248
02249 #ifdef DEBUG_MONSTERBUCKETS
02250
02251 for(uint index = bucketNumber+1; index < bucketNumber + 1 + extent; ++index) {
02252 Q_ASSERT(!m_fastBuckets[index]);
02253 }
02254 #endif
02255
02256 }else{
02257 Q_ASSERT(bucketPtr->monsterBucketExtent());
02258 Q_ASSERT(bucketPtr->isEmpty());
02259 uint oldExtent = bucketPtr->monsterBucketExtent();
02260 deleteBucket(bucketNumber);
02261
02262 for(uint index = bucketNumber; index < bucketNumber + 1 + oldExtent; ++index) {
02263 Q_ASSERT(!m_fastBuckets[index]);
02264 m_fastBuckets[index] = new MyBucket();
02265
02266 m_fastBuckets[index]->initialize(0);
02267 Q_ASSERT(!m_fastBuckets[index]->monsterBucketExtent());
02268 }
02269 }
02270 return m_fastBuckets[bucketNumber];
02271 }
02272
02273 MyBucket* bucketForIndex(short unsigned int index) const {
02274 MyBucket* bucketPtr = m_fastBuckets[index];
02275 if(!bucketPtr) {
02276 initializeBucket(index);
02277 bucketPtr = m_fastBuckets[index];
02278 }
02279 return bucketPtr;
02280 }
02281
02282 virtual bool open(const QString& path) {
02283 QMutexLocker lock(m_mutex);
02284
02285 close();
02286 m_currentOpenPath = path;
02287
02288 QDir dir(path);
02289 m_file = new QFile(dir.absoluteFilePath( m_repositoryName ));
02290 m_dynamicFile = new QFile(dir.absoluteFilePath( m_repositoryName + "_dynamic" ));
02291 if(!m_file->open( QFile::ReadWrite ) || !m_dynamicFile->open( QFile::ReadWrite ) ) {
02292 delete m_file;
02293 m_file = 0;
02294 delete m_dynamicFile;
02295 m_dynamicFile = 0;
02296 return false;
02297 }
02298
02299 m_metaDataChanged = true;
02300 if(m_file->size() == 0) {
02301
02302 m_file->resize(0);
02303 m_file->write((char*)&m_repositoryVersion, sizeof(uint));
02304 uint hashSize = bucketHashSize;
02305 m_file->write((char*)&hashSize, sizeof(uint));
02306 uint itemRepositoryVersion = staticItemRepositoryVersion();
02307 m_file->write((char*)&itemRepositoryVersion, sizeof(uint));
02308
02309 m_statBucketHashClashes = m_statItemCount = 0;
02310
02311 m_file->write((char*)&m_statBucketHashClashes, sizeof(uint));
02312 m_file->write((char*)&m_statItemCount, sizeof(uint));
02313
02314 m_buckets.resize(10);
02315 m_buckets.fill(0);
02316 uint bucketCount = m_buckets.size();
02317 m_file->write((char*)&bucketCount, sizeof(uint));
02318
02319 m_firstBucketForHash = new short unsigned int[bucketHashSize];
02320 memset(m_firstBucketForHash, 0, bucketHashSize * sizeof(short unsigned int));
02321
02322 m_currentBucket = 1;
02323 m_file->write((char*)&m_currentBucket, sizeof(uint));
02324 m_file->write((char*)m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize);
02325
02326 Q_ASSERT(m_file->pos() == BucketStartOffset);
02327
02328 m_freeSpaceBucketsSize = 0;
02329 m_dynamicFile->write((char*)&m_freeSpaceBucketsSize, sizeof(uint));
02330 m_freeSpaceBuckets.clear();
02331 }else{
02332 m_file->close();
02333 bool res = m_file->open( QFile::ReadOnly );
02334 VERIFY(res);
02335
02336 uint storedVersion = 0, hashSize = 0, itemRepositoryVersion = 0;
02337
02338 m_file->read((char*)&storedVersion, sizeof(uint));
02339 m_file->read((char*)&hashSize, sizeof(uint));
02340 m_file->read((char*)&itemRepositoryVersion, sizeof(uint));
02341 m_file->read((char*)&m_statBucketHashClashes, sizeof(uint));
02342 m_file->read((char*)&m_statItemCount, sizeof(uint));
02343
02344 if(storedVersion != m_repositoryVersion || hashSize != bucketHashSize || itemRepositoryVersion != staticItemRepositoryVersion()) {
02345 kDebug() << "repository" << m_repositoryName << "version mismatch in" << m_file->fileName() << ", stored: version " << storedVersion << "hashsize" << hashSize << "repository-version" << itemRepositoryVersion << " current: version" << m_repositoryVersion << "hashsize" << bucketHashSize << "repository-version" << staticItemRepositoryVersion();
02346 delete m_file;
02347 m_file = 0;
02348 delete m_dynamicFile;
02349 m_dynamicFile = 0;
02350 return false;
02351 }
02352 m_metaDataChanged = false;
02353
02354 uint bucketCount;
02355 m_file->read((char*)&bucketCount, sizeof(uint));
02356 m_buckets.resize(bucketCount);
02357 m_buckets.fill(0);
02358 m_file->read((char*)&m_currentBucket, sizeof(uint));
02359
02360 m_firstBucketForHash = new short unsigned int[bucketHashSize];
02361 m_file->read((char*)m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize);
02362
02363 Q_ASSERT(m_file->pos() == BucketStartOffset);
02364
02365 m_dynamicFile->read((char*)&m_freeSpaceBucketsSize, sizeof(uint));
02366 m_freeSpaceBuckets.resize(m_freeSpaceBucketsSize);
02367 m_dynamicFile->read((char*)m_freeSpaceBuckets.data(), sizeof(uint) * m_freeSpaceBucketsSize);
02368 }
02369
02370 #ifdef ITEMREPOSITORY_USE_MMAP_LOADING
02371 m_fileMap = m_file->map(BucketStartOffset, m_file->size() - BucketStartOffset);
02372 Q_ASSERT(m_file->isOpen());
02373 Q_ASSERT(m_file->size() >= BucketStartOffset);
02374 if(m_fileMap) {
02375 m_fileMapSize = m_file->size() - BucketStartOffset;
02376 }else{
02377 kWarning() << "mapping" << m_file->fileName() << "FAILED!";
02378 }
02379 #else
02380 m_fileMap = 0;
02381 m_fileMapSize = 0;
02382 #endif
02383
02384 m_file->close();
02385 m_dynamicFile->close();
02386
02387 m_fastBuckets = m_buckets.data();
02388 m_bucketCount = m_buckets.size();
02389 return true;
02390 }
02391
02393 virtual void close(bool doStore = false) {
02394 m_currentOpenPath.clear();
02395
02396 if(doStore)
02397 store();
02398
02399 if(m_file)
02400 m_file->close();
02401 delete m_file;
02402 m_file = 0;
02403 m_fileMap = 0;
02404 m_fileMapSize = 0;
02405
02406 if(m_dynamicFile)
02407 m_dynamicFile->close();
02408 delete m_dynamicFile;
02409 m_dynamicFile = 0;
02410
02411 delete[] m_firstBucketForHash;
02412
02415
02416
02417
02418
02419 m_buckets.clear();
02420 m_firstBucketForHash = 0;
02421 }
02422
02423 struct AllItemsReachableVisitor {
02424 AllItemsReachableVisitor(ItemRepository* rep) : repository(rep) {
02425 }
02426
02427 bool operator()(const Item* item) {
02428 return repository->itemReachable(item);
02429 }
02430
02431 ItemRepository* repository;
02432 };
02433
02434
02435 bool itemReachable(const Item* item) const {
02436 uint hash = item->hash();
02437
02438 short unsigned int bucket = *(m_firstBucketForHash + (hash % bucketHashSize));
02439
02440 while(bucket) {
02441
02442 MyBucket* bucketPtr = m_fastBuckets[bucket];
02443 if(!bucketPtr) {
02444 initializeBucket(bucket);
02445 bucketPtr = m_fastBuckets[bucket];
02446 }
02447
02448 if(bucketPtr->itemReachable(item, hash))
02449 return true;
02450
02451 bucket = bucketPtr->nextBucketForHash(hash);
02452 }
02453
02454 return false;
02455 }
02456
02457
02458 bool allItemsReachable(unsigned short bucket) {
02459 if(!bucket)
02460 return true;
02461
02462 MyBucket* bucketPtr = bucketForIndex(bucket);
02463
02464 AllItemsReachableVisitor visitor(this);
02465 return bucketPtr->visitAllItems(visitor);
02466 }
02467
02468 struct CleanupVisitor {
02469 bool operator()(const Item* item) {
02470 if(!ItemRequest::persistent(item)) {
02471
02472 }
02473 return true;
02474 }
02475 };
02476
02477 virtual int finalCleanup() {
02478 ThisLocker lock(m_mutex);
02479
02480 int changed = 0;
02481 for(uint a = 1; a <= m_currentBucket; ++a) {
02482 MyBucket* bucket = bucketForIndex(a);
02483 if(bucket && bucket->dirty()) {
02484 changed += bucket->finalCleanup(*this);
02485 }
02486 a += bucket->monsterBucketExtent();
02487 }
02488
02489 return changed;
02490 }
02491
02492 inline void initializeBucket(unsigned int bucketNumber) const {
02493 Q_ASSERT(bucketNumber);
02494 #ifdef DEBUG_MONSTERBUCKETS
02495 for(int offset = 1; offset < 5; ++offset) {
02496 int test = ((int)bucketNumber) - offset;
02497 if(test >= 0 && m_fastBuckets[test]) {
02498 Q_ASSERT(m_fastBuckets[test]->monsterBucketExtent() < offset);
02499 }
02500 }
02501 #endif
02502
02503 if(!m_fastBuckets[bucketNumber]) {
02504 m_fastBuckets[bucketNumber] = new MyBucket();
02505
02506 bool doMMapLoading = (bool)m_fileMap;
02507
02508 uint offset = ((bucketNumber-1) * MyBucket::DataSize);
02509 if(m_file && offset < m_fileMapSize && doMMapLoading && *((uint*)(m_fileMap + offset)) == 0) {
02510
02511 m_fastBuckets[bucketNumber]->initializeFromMap(((char*)m_fileMap) + offset);
02512 } else if(m_file) {
02513
02514
02515 bool res = m_file->open( QFile::ReadOnly );
02516
02517 if(offset + BucketStartOffset < m_file->size()) {
02518 VERIFY(res);
02519 offset += BucketStartOffset;
02520 m_file->seek(offset);
02521 uint monsterBucketExtent;
02522 m_file->read((char*)(&monsterBucketExtent), sizeof(unsigned int));;
02523 m_file->seek(offset);
02524 QByteArray data = m_file->read((1+monsterBucketExtent) * MyBucket::DataSize);
02525 m_fastBuckets[bucketNumber]->initializeFromMap(data.data());
02526 m_fastBuckets[bucketNumber]->prepareChange();
02527 }else{
02528 m_fastBuckets[bucketNumber]->initialize(0);
02529 }
02530
02531 m_file->close();
02532
02533 }else{
02534 m_fastBuckets[bucketNumber]->initialize(0);
02535 }
02536 }else{
02537 m_fastBuckets[bucketNumber]->initialize(0);
02538 }
02539 }
02540
02542 void deleteBucket(unsigned int bucketNumber) {
02543 Q_ASSERT(bucketForIndex(bucketNumber)->isEmpty());
02544 Q_ASSERT(bucketForIndex(bucketNumber)->noNextBuckets());
02545 delete m_fastBuckets[bucketNumber];
02546 m_fastBuckets[bucketNumber] = 0;
02547 }
02548
02549
02550 void storeBucket(unsigned int bucketNumber) const {
02551 if(m_file && m_fastBuckets[bucketNumber]) {
02552 m_fastBuckets[bucketNumber]->store(m_file, BucketStartOffset + (bucketNumber-1) * MyBucket::DataSize);
02553 }
02554 }
02555
02558 bool walkBucketLinks(uint checkBucket, uint hash, uint mustFindBucket = 0) const {
02559 bool found = false;
02560 while(checkBucket) {
02561 if(checkBucket == mustFindBucket)
02562 found = true;
02563
02564 checkBucket = bucketForIndex(checkBucket)->nextBucketForHash(hash);
02565 }
02566 return found || (mustFindBucket == 0);
02567 }
02568
02572 QPair<unsigned int, unsigned int> hashChainIntersection(uint mainHead, uint intersectorHead, uint hash) const {
02573 uint previous = 0;
02574 uint current = mainHead;
02575 while(current) {
02577 if(walkBucketLinks(intersectorHead, hash, current))
02578 return qMakePair(previous, current);
02579
02580 previous = current;
02581 current = bucketForIndex(current)->nextBucketForHash(hash);
02582 }
02583 return qMakePair(0u, 0u);
02584 }
02585
02586 void putIntoFreeList(unsigned short bucket, MyBucket* bucketPtr) {
02587 Q_ASSERT(!bucketPtr->monsterBucketExtent());
02588 int indexInFree = m_freeSpaceBuckets.indexOf(bucket);
02589
02590 if(indexInFree == -1 && (bucketPtr->freeItemCount() >= MyBucket::MinFreeItemsForReuse || bucketPtr->largestFreeSize() >= MyBucket::MinFreeSizeForReuse)) {
02591
02592
02593
02594 Q_ASSERT(bucketPtr->largestFreeSize());
02595 uint insertPos;
02596 for(insertPos = 0; insertPos < m_freeSpaceBucketsSize; ++insertPos) {
02597 if(bucketForIndex(m_freeSpaceBuckets[insertPos])->largestFreeSize() > bucketPtr->largestFreeSize())
02598 break;
02599 }
02600
02601 m_freeSpaceBuckets.insert(insertPos, bucket);
02602 ++m_freeSpaceBucketsSize;
02603 updateFreeSpaceOrder(insertPos);
02604 }else if(indexInFree != -1) {
02606 updateFreeSpaceOrder(indexInFree);
02607 }
02608 #ifdef DEBUG_MONSTERBUCKETS
02609 if(bucketPtr->isEmpty()) {
02610 Q_ASSERT(m_freeSpaceBuckets.contains(bucket));
02611 }
02612 #endif
02613 }
02614
02615 bool m_metaDataChanged;
02616 mutable QMutex m_ownMutex;
02617 mutable QMutex* m_mutex;
02618 QString m_repositoryName;
02619 unsigned int m_size;
02620 mutable uint m_currentBucket;
02621
02622 QVector<uint> m_freeSpaceBuckets;
02623 uint m_freeSpaceBucketsSize;
02624 mutable QVector<MyBucket* > m_buckets;
02625 mutable MyBucket** m_fastBuckets;
02626 mutable uint m_bucketCount;
02627 uint m_statBucketHashClashes, m_statItemCount;
02628 unsigned int m_bucketHashSize;
02629
02630 short unsigned int* m_firstBucketForHash;
02631
02632 QString m_currentOpenPath;
02633 ItemRepositoryRegistry* m_registry;
02634
02635 QFile* m_file;
02636 uchar* m_fileMap;
02637 uint m_fileMapSize;
02638
02639 QFile* m_dynamicFile;
02640 uint m_repositoryVersion;
02641 bool m_unloadingEnabled;
02642 AbstractRepositoryManager* m_manager;
02643 };
02644
02645 }
02646
02647 #endif