• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDevelop Platform Libraries
  • Sitemap
  • Contact Us
 

language/duchain

itemrepository.h

00001 /*
00002    Copyright 2008 David Nolden <david.nolden.kdevelop@art-master.de>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016    Boston, MA 02110-1301, USA.
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 //#define DEBUG_MONSTERBUCKETS
00036 
00037 // #define DEBUG_ITEMREPOSITORY_LOADING
00038 // #define ifDebugInfiniteRecursion(x) x
00039 #define ifDebugInfiniteRecursion(x)
00040 
00041 // #define ifDebugLostSpace(x) x
00042 #define ifDebugLostSpace(x)
00043 // #define DEBUG_INCORRECT_DELETE
00044 
00045 //Makes sure that all items stay reachable through the basic hash
00046 // #define DEBUG_ITEM_REACHABILITY
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 // #define DEBUG_HASH_SEQUENCES
00058 
00059 #define ITEMREPOSITORY_USE_MMAP_LOADING
00060 
00061 //Assertion macro that prevents warnings if debugging is disabled
00062 //Only use it to verify values, it should not call any functions, since else the function will even be called in release mode
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 // #define DEBUG_WRITING_EXTENTS
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       //Don't do this, we don't need it, and it may lead to crashes
00209 //       deleteRepository();
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   //Every item has to implement this function, and return a valid hash.
00248   //Must be exactly the same hash value as ExampleItemRequest::hash() has returned while creating the item.
00249   unsigned int hash() const {
00250     return 0;
00251   }
00252 
00253   //Every item has to implement this function, and return the complete size this item takes in memory.
00254   //Must be exactly the same value as ExampleItemRequest::itemSize() has returned while creating the item.
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 //This should be the approximate average size of an Item
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* /*item*/) const {
00293   }
00294   static void destroy(ExampleItem* /*item*/, AbstractItemRepository&) {
00295   }
00296   
00298   static bool persistent(ExampleItem*) {
00299     return true; //If this item should be kept, return true, else false
00300   }
00301 
00303   bool equals(const ExampleItem* /*item*/) 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, //When less than this count of free items in one buckets is reached, the bucket is removed from the global list of buckets with free items
00317       MaxFreeSizeForHide = fixedItemSize ? fixedItemSize : 0, //Only when the largest free size is smaller then this, the bucket is taken from the free list
00318       MinFreeItemsForReuse = 10,//When this count of free items in one bucket is reached, consider re-assigning them to new requests
00319       MinFreeSizeForReuse = ItemRepositoryBucketSize/20 //When the largest free item is bigger then this, the bucket is automatically added to the free list
00320     };
00321     enum {
00322       NextBucketHashSize = ObjectMapSize, //Affects the average count of bucket-chains that need to be walked in ItemRepository::index. Must be a multiple of 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         //The bigger we make the map, the lower the probability of a clash(and thus bad performance). However it increases memory usage.
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     //Tries to find the index this item has in this bucket, or returns zero if the item isn't there yet.
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       //Walk the chain of items with the same local hash
00471       while(index && (follower = followerIndex(index)) && !(request.equals(itemFromIndex(index))))
00472         index = follower;
00473 
00474       if(index && request.equals(itemFromIndex(index))) {
00475         return index; //We have found the item
00476       }
00477 
00478       return 0;
00479     }
00480 
00481     //Tries to get the index within this bucket, or returns zero. Will put the item into the bucket if there is room.
00482     //Created indices will never begin with 0xffff____, so you can use that index-range for own purposes.
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       //Walk the chain of items with the same local hash
00492       while(index && (follower = followerIndex(index)) && !(request.equals(itemFromIndex(index))))
00493         index = follower;
00494 
00495       if(index && request.equals(itemFromIndex(index)))
00496         return index; //We have found the item
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       //The second condition is needed, else we can get problems with zero-length items and an overflow in insertedAt to zero
00527       if(totalSize > m_available || (!itemSize && totalSize == m_available)) {
00528         //Try finding the smallest freed item that can hold the data
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             //The item also fits into the smaller follower, so use that one
00539             previousIndex = currentIndex;
00540             currentIndex = follower;
00541           }else{
00542             //The item fits into currentIndex, but not into the follower. So use currentIndex
00543             freeChunkSize = freeSize(currentIndex) - itemSize;
00544 
00545             //We need 2 bytes to store the free size
00546             if(freeChunkSize != 0 && freeChunkSize < AdditionalSpacePerItem+2) {
00547               //we can not manage the resulting free chunk as a separate item, so we cannot use this position.
00548               //Just pick the biggest free item, because there we can be sure that
00549               //either we can manage the split, or we cannot do anything at all in this bucket.
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; //Took one free item out of the chain
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           //Insert the resulting free chunk into the list of free items, so we don't lose it
00582           if(isBehindFreeSpace(currentIndex)) {
00583             //Create the free item at the beginning of currentIndex, so it can be merged with the free space in front
00584             freeItemPosition = currentIndex;
00585             currentIndex += freeItemSize + AdditionalSpacePerItem;
00586           }else{
00587             //Create the free item behind currentIndex
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         //We have to insert the item
00598         insertedAt = ItemRepositoryBucketSize - m_available;
00599 
00600         insertedAt += AdditionalSpacePerItem; //Room for the prepended follower-index
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       //Last thing we do, because createItem may recursively do even more transformation of the repository
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         //If this assertion triggers, then the item writes a bigger range than it advertised in
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; //Clash
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 //             kDebug() << "follower-chain at" << a << ":" << length;
00713             
00714             longestInBucketFollowerChain = length;
00715           }
00716         }
00717       }
00718     }
00719     
00720     //A version of hasClashingItem that visits all items naively without using any assumptions.
00721     //This was used to debug hasClashingItem, but should not be used otherwise.
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     //Returns whether the given item is reachabe within this bucket, through its hash.
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       //Step 1: Remove the item from the data-structures that allow finding it: m_objectMap
00772       unsigned short localHash = hash % m_objectMapSize;
00773       unsigned short currentIndex = m_objectMap[localHash];
00774       unsigned short previousIndex = 0;
00775 
00776       //Fix the follower-link by setting the follower of the previous item to the next one, or updating m_objectMap
00777       while(currentIndex != index) {
00778         previousIndex = currentIndex;
00779         currentIndex = followerIndex(currentIndex);
00780         //If this assertion triggers, the deleted item was not registered under the given hash
00781         Q_ASSERT(currentIndex);
00782       }
00783       Q_ASSERT(currentIndex == index);
00784 
00785       if(!previousIndex)
00786         //The item was directly in the object map
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); //For debugging, so we notice the data is wrong
00802 
00803       if(m_monsterBucketExtent) {
00805         Q_ASSERT(!m_available);
00806         m_available = ItemRepositoryBucketSize;
00807 
00808         //Items are always inserted into monster-buckets at a fixed position
00809         Q_ASSERT(currentIndex == AdditionalSpacePerItem);
00810         Q_ASSERT(m_objectMap[localHash] == 0);
00811       }else{
00813         setFreeSize(index, size);
00814 
00815         //Try merging the created free item to other free items around, else add it into the free list
00816         insertFreeItem(index);
00817 
00818         if(m_freeItemCount == 1 && freeSize(m_largestFreeItem) + m_available == ItemRepositoryBucketSize) {
00819           //Everything has been deleted, there is only free space left. Make the bucket empty again,
00820           //so it can later also be used as a monster-bucket.
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       //Make sure the item cannot be found any more
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); //The item must not be found
00840       }
00841 #endif
00842 //       Q_ASSERT(canAllocateItem(size));
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           //Get the follower early, so there is no problems when the current
00880           //index is removed
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             //Get the follower early, so there is no problems when the current
00903             //index is removed
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; //Set to dirty so we re-iterate
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     //Size of the largest item that could be inserted into this bucket
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         //Either we need an exact match, or 2 additional bytes to manage the resulting gap
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     //How many ticks ago the item was last used
00978     int lastUsed() const {
00979       return m_lastUsed;
00980     }
00981 
00982     //Whether this bucket was changed since it was last stored
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     //Counts together the space that is neither accessible through m_objectMap nor through the free items
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       //If the item-size is fixed, we don't need to do any management. Just keep a list of free items. Items of other size will never be requested.
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           //Merge behind index
01077           if(currentIndex == index + freeSize(index) + AdditionalSpacePerItem) {
01078 
01079             //Remove currentIndex from the free chain, since it's merged backwards into index
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; //One was removed
01089 
01090             //currentIndex is directly behind index, touching its space. Merge them.
01091             setFreeSize(index, freeSize(index) + AdditionalSpacePerItem + freeSize(currentIndex));
01092 
01093             //Recurse to do even more merging
01094             insertFreeItem(index);
01095             return;
01096           }
01097 
01098           //Merge before index
01099           if(index == currentIndex + freeSize(currentIndex) + AdditionalSpacePerItem) {
01100 
01101             if(previousIndex && followerIndex(currentIndex))
01102               Q_ASSERT(freeSize(previousIndex) >= freeSize(followerIndex(currentIndex)));
01103 
01104             //Remove currentIndex from the free chain, since insertFreeItem wants
01105             //it not to be in the chain, and it will be inserted in another place
01106             if(previousIndex)
01107               setFollowerIndex(previousIndex, followerIndex(currentIndex));
01108             else
01109               m_largestFreeItem = followerIndex(currentIndex);
01110 
01111             --m_freeItemCount; //One was removed
01112 
01113             //index is directly behind currentIndex, touching its space. Merge them.
01114             setFreeSize(currentIndex, freeSize(currentIndex) + AdditionalSpacePerItem + freeSize(index));
01115 
01116             //Recurse to do even more merging
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         //Insert the free item into the chain opened by m_largestFreeItem
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); //must not be in the chain yet
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           //This item is larger than all already registered free items, or there are none.
01157           m_largestFreeItem = index;
01158       }else{
01159         Q_ASSERT(freeSize(index) == fixedItemSize);
01160         //When all items have the same size, just prepent to the front.
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     // Only returns the current value if the item is actually free
01193     inline unsigned short freeSize(unsigned short index) const {
01194       return *((unsigned short*)(m_data+index));
01195     }
01196 
01197     //Convenience function to set the free-size, only for freed items
01198     void setFreeSize(unsigned short index, unsigned short size) {
01199       *((unsigned short*)(m_data+index)) = size;
01200     }
01201 
01202     uint m_monsterBucketExtent; //If this is a monster-bucket, this contains the count of follower-buckets that belong to this one
01203     unsigned int m_available;
01204     char* m_data; //Structure of the data: <Position of next item with same hash modulo ItemRepositoryBucketSize>(2 byte), <Item>(item.size() byte)
01205     char* m_mappedData; //Read-only memory-mapped data. If this equals m_data, m_data must not be written
01206     short unsigned int* m_objectMap; //Points to the first object in m_data with (hash % m_objectMapSize) == index. Points to the item itself, so subtract 1 to get the pointer to the next item with same local hash.
01207     uint m_objectMapSize;
01208     short unsigned int m_largestFreeItem; //Points to the largest item that is currently marked as free, or zero. That one points to the next largest one through followerIndex
01209     unsigned int m_freeItemCount;
01210 
01211     unsigned short* m_nextBucketHash;
01212 
01213     bool m_dirty; //Whether the data was changed since the last finalCleanup
01214     bool m_changed; //Whether this bucket was changed since it was last stored to disk
01215     mutable int m_lastUsed; //How many ticks ago this bucket was last accessed
01216 };
01217 
01218 template<bool lock>
01219 struct Locker { //This is a dummy that does nothing
01220   template<class T>
01221   Locker(const T& /*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 //       kDebug() << "enabling" << i << "to" << (void*)(((char*)i)+size);
01247     }
01248     
01249     ~DynamicItem() {
01250       if(m_start) {
01251 //         kDebug() << "destructor-disabling" << m_item;
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 //         kDebug() << "stealing" << m_item;
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     //Must be a multiple of Bucket::ObjectMapSize, so Bucket::hasClashingItem can be computed
01291     //Must also be a multiple of Bucket::NextBucketHashSize, for the same reason.(Currently those are same)
01292     bucketHashSize = (targetBucketHashSize / MyBucket::ObjectMapSize) * MyBucket::ObjectMapSize
01293   };
01294 
01295   enum {
01296     BucketStartOffset = sizeof(uint) * 7 + sizeof(short unsigned int) * bucketHashSize //Position in the data where the bucket array starts
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; //Skip the first bucket, we won't use it so we have the zero indices for special purposes
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; //Whether a bucket was picked for re-use that already is in the hash chain
01350     int reOrderFreeSpaceBucketIndex = -1;
01351 
01352     while(previousBucketNumber) {
01353       //We have a bucket that contains an item with the given hash % bucketHashSize, so check if the item is already there
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         //We've found the item, it's already there
01363         return (previousBucketNumber << 16) + indexInBucket; //Combine the index in the bucket, and the bucker number into one index
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           //Remember that this bucket has enough space to store the item, if it isn't already stored.
01374           //This gives a better structure, and saves us from cyclic follower structures.
01375           useBucket = previousBucketNumber;
01376           reOrderFreeSpaceBucketIndex = m_freeSpaceBuckets.indexOf(useBucket);
01377           pickedBucketInChain = true;
01378         }
01379         //The item isn't in bucket previousBucketNumber, but maybe the bucket has a pointer to the next bucket that might contain the item
01380         //Should happen rarely
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       //Try finding an existing bucket with deleted space to store the data into
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           //The item fits into the bucket.
01400           useBucket = m_freeSpaceBuckets[a];
01401           reOrderFreeSpaceBucketIndex = a;
01402           break;
01403         }
01404       }
01405     }
01406 
01407     //The item isn't in the repository yet, find a new bucket for it
01408     while(1) {
01409       if(useBucket >= m_bucketCount) {
01410           if(m_bucketCount >= 0xfffe) { //We have reserved the last bucket index 0xffff for special purposes
01411           //the repository has overflown.
01412           kWarning() << "Found no room for an item in" << m_repositoryName << "size of the item:" << request.itemSize();
01413           return 0;
01414         }else{
01415           //Allocate new buckets
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       //If we could not allocate the item in an empty bucket, then we need to create a monster-bucket that
01435       //can hold the data.
01436       if(bucketPtr->isEmpty() && !indexInBucket) {
01438         uint totalSize = size + MyBucket::AdditionalSpacePerItem;
01439 
01440         Q_ASSERT((totalSize > ItemRepositoryBucketSize));
01441 
01442         useBucket = 0;
01443         //The item did not fit in, we need a monster-bucket(Merge consecutive buckets)
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             //This bucket is a candidate for monster-bucket merging
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           //Create a new monster-bucket at the end of the data
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           //Should happen rarely
01509           ++m_statBucketHashClashes;
01510 
01512           ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, previousBucketNumber));)
01513 
01514           //Find the position where the paths of useBucket and *bucketHashPosition intersect, and insert useBucket
01515           //there. That way, we don't create loops.
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             //State: intersect.first == 0 && intersect.second != 0. This means that whole compleet
01550             //chain opened by *bucketHashPosition with the hash-value is also following useBucket,
01551             //so useBucket can just be inserted at the top
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; //Combine the index in the bucket, and the bucker number into one index
01569       }else{
01570         //This should never happen when we picked a bucket for re-use
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     //We haven't found a bucket that already contains the item, so append it to the current bucket
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       //We have a bucket that contains an item with the given hash % bucketHashSize, so check if the item is already there
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         //We've found the item, it's already there
01610         return (previousBucketNumber << 16) + indexInBucket; //Combine the index in the bucket, and the bucker number into one index
01611       } else {
01612         //The item isn't in bucket previousBucketNumber, but maybe the bucket has a pointer to the next bucket that might contain the item
01613         //Should happen rarely
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); //We don't use zero buckets
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     //Apart from removing the item itself, we may have to recreate the nextBucketForHash link, so we need the previous bucket
01647 
01648     while(previousBucketNumber) {
01649       //We have a bucket that contains an item with the given hash % bucketHashSize, so check if the item is already there
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; //Now previousBucketNumber
01661       else
01662         previousBucketNumber = nextBucket;
01663     }
01664 
01665     //Make sure the index was reachable through the hashes
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     //return; ///@todo Find out what this problem is about. If we don't return here, some items become undiscoverable through hashes after some time
01683     if(previousBucketNumber == 0) {
01684       //The item is directly in the m_firstBucketForHash hash
01685       //Put the next item in the nextBucketForHash chain into m_firstBucketForHash that has a hash clashing in that array.
01686       Q_ASSERT(*bucketHashPosition == bucket);
01687       unsigned short previous = bucket;
01688       while(!bucketPtr->hasClashingItem(hash, bucketHashSize))
01689       {
01690 //         Q_ASSERT(!bucketPtr->hasClashingItemReal(hash, bucketHashSize));
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 //         Q_ASSERT(!bucketPtr->hasClashingItemReal(hash, MyBucket::NextBucketHashSize));
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       //Convert the monster-bucket back to multiple normal buckets, and put them into the free list
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); //We don't use zero buckets
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); //We don't use zero buckets
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); //We don't use zero buckets
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); //We don't use zero buckets
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; //How big the hash is
01877     uint hashUse; //How many slots in the hash are used
01878     uint averageInBucketHashSize;
01879     uint averageInBucketUsedSlotCount;
01880     float averageInBucketSlotChainLength;
01881     uint longestInBucketChain;
01882     
01883     uint longestNextBucketChain;
01884     uint totalBucketFollowerSlots; //Total count of used slots in the nextBucketForHash structure
01885     float averageNextBucketForHashSequenceLength; //Average sequence length of a nextBucketForHash sequence(If not empty)
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; //Should be zero, else something is wrong
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 //             kDebug() << "next-bucket-chain in" << a << aa << ":" << length;
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     //If m_statBucketHashClashes is high, the bucket-hash needs to be bigger
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 //   #ifdef DEBUG_ITEMREPOSITORY_LOADING
02115 //         {
02116 //           m_file->flush();
02117 //           m_file->seek(0);
02118 //           uint storedVersion, hashSize, itemRepositoryVersion, statBucketHashClashes, statItemCount;
02119 //
02120 //           m_file->read((char*)&storedVersion, sizeof(uint));
02121 //           m_file->read((char*)&hashSize, sizeof(uint));
02122 //           m_file->read((char*)&itemRepositoryVersion, sizeof(uint));
02123 //           m_file->read((char*)&statBucketHashClashes, sizeof(uint));
02124 //           m_file->read((char*)&statItemCount, sizeof(uint));
02125 //           Q_ASSERT(storedVersion == m_repositoryVersion);
02126 //           Q_ASSERT(hashSize == bucketHashSize);
02127 //           Q_ASSERT(itemRepositoryVersion == ItemRepositoryVersion);
02128 //           Q_ASSERT(statBucketHashClashes == m_statBucketHashClashes);
02129 //           Q_ASSERT(statItemCount == m_statItemCount);
02130 //
02131 //           uint bucketCount, currentBucket;
02132 //           m_file->read((char*)&bucketCount, sizeof(uint));
02133 //           Q_ASSERT(bucketCount == (uint)m_buckets.size());
02134 //           m_file->read((char*)&currentBucket, sizeof(uint));
02135 //           Q_ASSERT(currentBucket == m_currentBucket);
02136 //
02137 //           short unsigned int* s = new short unsigned int[bucketHashSize];
02138 //           m_file->read((char*)s, sizeof(short unsigned int) * bucketHashSize);
02139 //           Q_ASSERT(memcmp(s, m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize) == 0);
02140 //           Q_ASSERT(m_file->pos() == BucketStartOffset);
02141 //           delete[] s;
02142 //         }
02143 //   #endif
02144       }
02145       //To protect us from inconsistency due to crashes. flush() is not enough. We need to close.
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       //Remove the item from freeSpaceBuckets
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           //This item should be behind the successor, either because it has a lower largestFreeSize, or because the index is lower
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           //This item should be behind the successor, either because it has a higher largestFreeSize, or because the index is higher
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       //Convert to monster-bucket
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); //Delete the monster-bucket
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     //kDebug() << "opening repository" << m_repositoryName << "at" << path;
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; //Skip the first bucket, we won't use it so we have the zero indices for special purposes
02323       m_file->write((char*)&m_currentBucket, sizeof(uint));
02324       m_file->write((char*)m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize);
02325       //We have completely initialized the file now
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 ); //Re-open in read-only mode, so we create a read-only m_fileMap
02334       VERIFY(res);
02335       //Check that the version is correct
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     //To protect us from inconsistency due to crashes. flush() is not enough.
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 //     for(int i = 0; i < m_buckets.size(); ++i) {
02416 //       delete m_buckets[i];
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   //Returns whether the given item is reachable through its hash
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   //Returns true if all items in the given bucket are reachable through their hashes
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         //Delete the item
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(); //Skip buckets that are attached as tail to monster-buckets
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 //         kDebug() << "loading bucket mmap:" << bucketNumber;
02511         m_fastBuckets[bucketNumber]->initializeFromMap(((char*)m_fileMap) + offset);
02512       } else if(m_file) {
02513         //Either memory-mapping is disabled, or the item is not in the existing memory-map, 
02514         //so we have to load it the classical way.
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   //m_file must be opened
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       //Add the bucket to the list of buckets from where to re-assign free space
02593       //We only do it when a specific threshold of empty items is reached, because that way items can stay "somewhat" semantically ordered.
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   //List of buckets that have free space available that can be assigned. Sorted by size: Smallest space first. Second order sorting: Bucket index
02622   QVector<uint> m_freeSpaceBuckets;
02623   uint m_freeSpaceBucketsSize; //for speedup
02624   mutable QVector<MyBucket* > m_buckets;
02625   mutable MyBucket** m_fastBuckets; //For faster access
02626   mutable uint m_bucketCount;
02627   uint m_statBucketHashClashes, m_statItemCount;
02628   unsigned int m_bucketHashSize;
02629   //Maps hash-values modulo 1<<bucketHashSizeBits to the first bucket such a hash-value appears in
02630   short unsigned int* m_firstBucketForHash;
02631 
02632   QString m_currentOpenPath;
02633   ItemRepositoryRegistry* m_registry;
02634   //File that contains the buckets
02635   QFile* m_file;
02636   uchar* m_fileMap;
02637   uint m_fileMapSize;
02638   //File that contains more dynamic data, like the list of buckets with deleted items
02639   QFile* m_dynamicFile;
02640   uint m_repositoryVersion;
02641   bool m_unloadingEnabled;
02642   AbstractRepositoryManager* m_manager;
02643 };
02644 
02645 }
02646 
02647 #endif

language/duchain

Skip menu "language/duchain"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDevelop Platform Libraries

Skip menu "KDevelop Platform Libraries"
  • interfaces
  • language
  •   codegen
  •   duchain
  •   editor
  • outputview
  • project
  • shell
  • sublime
  • util
  • vcs
Generated for KDevelop Platform Libraries by doxygen 1.5.9-20090814
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal