File indexing completed on 2024-12-22 04:10:33

0001 /*
0002  *  SPDX-FileCopyrightText: 2004 C. Boemann <cbo@boemann.dk>
0003  *  SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include <QtGlobal>
0009 #include "kis_debug.h"
0010 #include "kis_global.h"
0011 
0012 //#define SHARED_TILES_SANITY_CHECK
0013 
0014 
0015 template<class T>
0016 KisTileHashTableTraits<T>::KisTileHashTableTraits(KisMementoManager *mm)
0017         : m_lock(QReadWriteLock::NonRecursive)
0018 {
0019     m_hashTable = new TileTypeSP [TABLE_SIZE];
0020     Q_CHECK_PTR(m_hashTable);
0021 
0022     m_numTiles = 0;
0023     m_defaultTileData = 0;
0024     m_mementoManager = mm;
0025 }
0026 
0027 template<class T>
0028 KisTileHashTableTraits<T>::KisTileHashTableTraits(const KisTileHashTableTraits<T> &ht,
0029         KisMementoManager *mm)
0030         : m_lock(QReadWriteLock::NonRecursive)
0031 {
0032     QReadLocker locker(&ht.m_lock);
0033 
0034     m_mementoManager = mm;
0035     m_defaultTileData = 0;
0036     setDefaultTileDataImp(ht.m_defaultTileData);
0037 
0038     m_hashTable = new TileTypeSP [TABLE_SIZE];
0039     Q_CHECK_PTR(m_hashTable);
0040 
0041 
0042     TileTypeSP foreignTile;
0043     TileTypeSP nativeTile;
0044     TileTypeSP nativeTileHead;
0045     for (qint32 i = 0; i < TABLE_SIZE; i++) {
0046         nativeTileHead = 0;
0047 
0048         foreignTile = ht.m_hashTable[i];
0049         while (foreignTile) {
0050             nativeTile = TileTypeSP(new TileType(*foreignTile, m_mementoManager));
0051             nativeTile->setNext(nativeTileHead);
0052             nativeTileHead = nativeTile;
0053 
0054             foreignTile = foreignTile->next();
0055         }
0056 
0057         m_hashTable[i] = nativeTileHead;
0058     }
0059     m_numTiles = ht.m_numTiles;
0060 }
0061 
0062 template<class T>
0063 KisTileHashTableTraits<T>::~KisTileHashTableTraits()
0064 {
0065     clear();
0066     delete[] m_hashTable;
0067     setDefaultTileDataImp(0);
0068 }
0069 
0070 template<class T>
0071 quint32 KisTileHashTableTraits<T>::calculateHash(qint32 col, qint32 row)
0072 {
0073     return ((row << 5) + (col & 0x1F)) & 0x3FF;
0074 }
0075 
0076 template<class T>
0077 typename KisTileHashTableTraits<T>::TileTypeSP
0078 KisTileHashTableTraits<T>::getTileMinefieldWalk(qint32 col, qint32 row, qint32 idx)
0079 {
0080     // WARNING: this function is here only for educational purposes! Don't
0081     //          use it! It causes race condition in a shared pointer copy-ctor
0082     //          when accessing m_hashTable!
0083 
0084     /**
0085      * This is a special method for dangerous and unsafe access to
0086      * the tiles table. Thanks to the fact that our shared pointers
0087      * are thread safe, we can iterate through the linked list without
0088      * having any locks help. In the worst case, we will miss the needed
0089      * tile. In that case, the higher level code will do the proper
0090      * locking and do the second try with all the needed locks held.
0091      */
0092 
0093     TileTypeSP headTile = m_hashTable[idx];
0094     TileTypeSP tile = headTile;
0095 
0096     for (; tile; tile = tile->next()) {
0097         if (tile->col() == col &&
0098             tile->row() == row) {
0099 
0100             if (m_hashTable[idx] != headTile) {
0101                 tile.clear();
0102             }
0103 
0104             break;
0105         }
0106     }
0107 
0108     return tile;
0109 }
0110 
0111 template<class T>
0112 typename KisTileHashTableTraits<T>::TileTypeSP
0113 KisTileHashTableTraits<T>::getTile(qint32 col, qint32 row, qint32 idx)
0114 {
0115     TileTypeSP tile = m_hashTable[idx];
0116 
0117     for (; tile; tile = tile->next()) {
0118         if (tile->col() == col &&
0119                 tile->row() == row) {
0120 
0121             return tile;
0122         }
0123     }
0124 
0125     return TileTypeSP();
0126 }
0127 
0128 template<class T>
0129 void KisTileHashTableTraits<T>::linkTile(TileTypeSP tile, qint32 idx)
0130 {
0131     TileTypeSP firstTile = m_hashTable[idx];
0132 
0133 #ifdef SHARED_TILES_SANITY_CHECK
0134     Q_ASSERT_X(!tile->next(), "KisTileHashTableTraits<T>::linkTile",
0135                "A tile can't be shared by several hash tables, sorry.");
0136 #endif
0137 
0138     tile->setNext(firstTile);
0139     m_hashTable[idx] = tile;
0140     m_numTiles++;
0141 }
0142 
0143 template<class T>
0144 bool KisTileHashTableTraits<T>::unlinkTile(qint32 col, qint32 row, qint32 idx)
0145 {
0146     TileTypeSP tile = m_hashTable[idx];
0147     TileTypeSP prevTile;
0148 
0149     for (; tile; tile = tile->next()) {
0150         if (tile->col() == col &&
0151                 tile->row() == row) {
0152 
0153             if (prevTile)
0154                 prevTile->setNext(tile->next());
0155             else
0156                 /* optimize here*/
0157                 m_hashTable[idx] = tile->next();
0158 
0159             /**
0160              * The shared pointer may still be accessed by someone, so
0161              * we need to disconnects the tile from memento manager
0162              * explicitly
0163              */
0164             tile->setNext(TileTypeSP());
0165             tile->notifyDetachedFromDataManager();
0166             tile.clear();
0167 
0168             m_numTiles--;
0169             return true;
0170         }
0171         prevTile = tile;
0172     }
0173 
0174     return false;
0175 }
0176 
0177 template<class T>
0178 inline void KisTileHashTableTraits<T>::setDefaultTileDataImp(KisTileData *defaultTileData)
0179 {
0180     if (m_defaultTileData) {
0181         m_defaultTileData->release();
0182         m_defaultTileData = 0;
0183     }
0184 
0185     if (defaultTileData) {
0186         defaultTileData->acquire();
0187         m_defaultTileData = defaultTileData;
0188     }
0189 }
0190 
0191 template<class T>
0192 inline KisTileData* KisTileHashTableTraits<T>::defaultTileDataImp() const
0193 {
0194     return m_defaultTileData;
0195 }
0196 
0197 
0198 template<class T>
0199 bool KisTileHashTableTraits<T>::tileExists(qint32 col, qint32 row)
0200 {
0201     return this->getExistingTile(col, row);
0202 }
0203 
0204 template<class T>
0205 typename KisTileHashTableTraits<T>::TileTypeSP
0206 KisTileHashTableTraits<T>::getExistingTile(qint32 col, qint32 row)
0207 {
0208     const qint32 idx = calculateHash(col, row);
0209 
0210     // NOTE: minefield walk is disabled due to supposed unsafety,
0211     //       see bug 391270
0212 
0213     QReadLocker locker(&m_lock);
0214     return getTile(col, row, idx);
0215 }
0216 
0217 template<class T>
0218 typename KisTileHashTableTraits<T>::TileTypeSP
0219 KisTileHashTableTraits<T>::getTileLazy(qint32 col, qint32 row,
0220                                        bool& newTile)
0221 {
0222     const qint32 idx = calculateHash(col, row);
0223 
0224     // NOTE: minefield walk is disabled due to supposed unsafety,
0225     //       see bug 391270
0226 
0227     newTile = false;
0228     TileTypeSP tile;
0229 
0230     {
0231         QReadLocker locker(&m_lock);
0232         tile = getTile(col, row, idx);
0233     }
0234 
0235     if (!tile) {
0236         QWriteLocker locker(&m_lock);
0237         tile = new TileType(col, row, m_defaultTileData, m_mementoManager);
0238         linkTile(tile, idx);
0239         newTile = true;
0240     }
0241 
0242     return tile;
0243 }
0244 
0245 template<class T>
0246 typename KisTileHashTableTraits<T>::TileTypeSP
0247 KisTileHashTableTraits<T>::getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile)
0248 {
0249     const qint32 idx = calculateHash(col, row);
0250 
0251     // NOTE: minefield walk is disabled due to supposed unsafety,
0252     //       see bug 391270
0253 
0254     QReadLocker locker(&m_lock);
0255 
0256     TileTypeSP tile = getTile(col, row, idx);
0257     existingTile = tile;
0258 
0259     if (!existingTile) {
0260         tile = new TileType(col, row, m_defaultTileData, 0);
0261     }
0262 
0263     return tile;
0264 }
0265 
0266 template<class T>
0267 void KisTileHashTableTraits<T>::addTile(TileTypeSP tile)
0268 {
0269     const qint32 idx = calculateHash(tile->col(), tile->row());
0270 
0271     QWriteLocker locker(&m_lock);
0272     linkTile(tile, idx);
0273 }
0274 
0275 template<class T>
0276 bool KisTileHashTableTraits<T>::deleteTile(qint32 col, qint32 row)
0277 {
0278     const qint32 idx = calculateHash(col, row);
0279 
0280     QWriteLocker locker(&m_lock);
0281     return unlinkTile(col, row, idx);
0282 }
0283 
0284 template<class T>
0285 bool KisTileHashTableTraits<T>::deleteTile(TileTypeSP tile)
0286 {
0287     return deleteTile(tile->col(), tile->row());
0288 }
0289 
0290 template<class T>
0291 void KisTileHashTableTraits<T>::clear()
0292 {
0293     QWriteLocker locker(&m_lock);
0294     TileTypeSP tile = TileTypeSP();
0295     qint32 i;
0296 
0297     for (i = 0; i < TABLE_SIZE; i++) {
0298         tile = m_hashTable[i];
0299 
0300         while (tile) {
0301             TileTypeSP tmp = tile;
0302             tile = tile->next();
0303 
0304             /**
0305              * About disconnection of tiles see a comment in unlinkTile()
0306              */
0307 
0308             tmp->setNext(TileTypeSP());
0309             tmp->notifyDetachedFromDataManager();
0310             tmp = 0;
0311 
0312             m_numTiles--;
0313         }
0314 
0315         m_hashTable[i] = 0;
0316     }
0317 
0318     Q_ASSERT(!m_numTiles);
0319 }
0320 
0321 template<class T>
0322 void KisTileHashTableTraits<T>::setDefaultTileData(KisTileData *defaultTileData)
0323 {
0324     QWriteLocker locker(&m_lock);
0325     setDefaultTileDataImp(defaultTileData);
0326 }
0327 
0328 template<class T>
0329 KisTileData* KisTileHashTableTraits<T>::defaultTileData() const
0330 {
0331     QWriteLocker locker(&m_lock);
0332     return defaultTileDataImp();
0333 }
0334 
0335 template <class T>
0336 inline KisTileData* KisTileHashTableTraits<T>::refAndFetchDefaultTileData() const
0337 {
0338     QWriteLocker locker(&m_lock);
0339     KisTileData *result = defaultTileDataImp();
0340     result->ref();
0341     return result;
0342 }
0343 
0344 
0345 /*************** Debugging stuff ***************/
0346 
0347 template<class T>
0348 void KisTileHashTableTraits<T>::debugPrintInfo()
0349 {
0350     if (!m_numTiles) return;
0351 
0352     qInfo() << "==========================\n"
0353              << "TileHashTable:"
0354              << "\n   def. data:\t\t" << m_defaultTileData
0355              << "\n   numTiles:\t\t" << m_numTiles;
0356     debugListLengthDistribution();
0357     qInfo() << "==========================\n";
0358 }
0359 
0360 template<class T>
0361 qint32 KisTileHashTableTraits<T>::debugChainLen(qint32 idx)
0362 {
0363     qint32 len = 0;
0364     for (TileTypeSP it = m_hashTable[idx]; it; it = it->next(), len++) ;
0365     return len;
0366 }
0367 
0368 template<class T>
0369 void KisTileHashTableTraits<T>::debugMaxListLength(qint32 &min, qint32 &max)
0370 {
0371     TileTypeSP tile;
0372     qint32 maxLen = 0;
0373     qint32 minLen = m_numTiles;
0374     qint32 tmp = 0;
0375 
0376     for (qint32 i = 0; i < TABLE_SIZE; i++) {
0377         tmp = debugChainLen(i);
0378         if (tmp > maxLen)
0379             maxLen = tmp;
0380         if (tmp < minLen)
0381             minLen = tmp;
0382     }
0383 
0384     min = minLen;
0385     max = maxLen;
0386 }
0387 
0388 template<class T>
0389 void KisTileHashTableTraits<T>::debugListLengthDistribution()
0390 {
0391     qint32 min, max;
0392     qint32 arraySize;
0393     qint32 tmp;
0394 
0395     debugMaxListLength(min, max);
0396     arraySize = max - min + 1;
0397 
0398     qint32 *array = new qint32[arraySize];
0399     memset(array, 0, sizeof(qint32)*arraySize);
0400 
0401     for (qint32 i = 0; i < TABLE_SIZE; i++) {
0402         tmp = debugChainLen(i);
0403         array[tmp-min]++;
0404     }
0405 
0406     qInfo() << QString("   minChain: %1\n").arg(min);
0407     qInfo() << QString("   maxChain: %1\n").arg(max);
0408 
0409     qInfo() << "   Chain size distribution:";
0410     for (qint32 i = 0; i < arraySize; i++)
0411         qInfo() << QString("      %1: %2").arg(i + min).arg(array[i]);
0412 
0413     delete[] array;
0414 }
0415 
0416 template<class T>
0417 void KisTileHashTableTraits<T>::sanityChecksumCheck()
0418 {
0419     /**
0420      * We assume that the lock should have already been taken
0421      * by the code that was going to change the table
0422      */
0423     Q_ASSERT(!m_lock.tryLockForWrite());
0424 
0425     TileTypeSP tile = 0;
0426     qint32 exactNumTiles = 0;
0427 
0428     for (qint32 i = 0; i < TABLE_SIZE; i++) {
0429         tile = m_hashTable[i];
0430         while (tile) {
0431             exactNumTiles++;
0432             tile = tile->next();
0433         }
0434     }
0435 
0436     if (exactNumTiles != m_numTiles) {
0437         dbgKrita << "Sanity check failed!";
0438         dbgKrita << ppVar(exactNumTiles);
0439         dbgKrita << ppVar(m_numTiles);
0440         dbgKrita << "Wrong tiles checksum!";
0441         Q_ASSERT(0); // not fatalKrita for a backtrace support
0442     }
0443 }