File indexing completed on 2024-05-26 04:28:05

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 #ifndef KIS_TILEHASHTABLE_H_
0009 #define KIS_TILEHASHTABLE_H_
0010 
0011 #include "kis_tile.h"
0012 
0013 
0014 
0015 /**
0016  * This is a  template for a hash table that stores  tiles (or some other
0017  * objects  resembling tiles).   Actually, this  object should  only have
0018  * col()/row() methods and be able to answer setNext()/next() requests to
0019  * be   stored   here.    It   is   used   in   KisTiledDataManager   and
0020  * KisMementoManager.
0021  */
0022 
0023 template<class T>
0024 class KisTileHashTableTraits
0025 {
0026 public:
0027     typedef T               TileType;
0028     typedef KisSharedPtr<T> TileTypeSP;
0029     typedef KisWeakSharedPtr<T> TileTypeWSP;
0030 
0031     KisTileHashTableTraits(KisMementoManager *mm);
0032     KisTileHashTableTraits(const KisTileHashTableTraits<T> &ht,
0033                            KisMementoManager *mm);
0034 
0035     /* virtual? */
0036     ~KisTileHashTableTraits();
0037 
0038     bool isEmpty() {
0039         return !m_numTiles;
0040     }
0041 
0042     bool tileExists(qint32 col, qint32 row);
0043 
0044     /**
0045      * Returns a tile in position (col,row). If no tile exists,
0046      * returns null.
0047      * \param col column of the tile
0048      * \param row row of the tile
0049      */
0050     TileTypeSP getExistingTile(qint32 col, qint32 row);
0051 
0052     /**
0053      * Returns a tile in position (col,row). If no tile exists,
0054      * creates a new one, attaches it to the list and returns.
0055      * \param col column of the tile
0056      * \param row row of the tile
0057      * \param newTile out-parameter, returns true if a new tile
0058      *                was created
0059      */
0060     TileTypeSP getTileLazy(qint32 col, qint32 row, bool& newTile);
0061 
0062     /**
0063      * Returns a tile in position (col,row). If no tile exists,
0064      * creates nothing, but returns shared default tile object
0065      * of the table. Be careful, this object has column and row
0066      * parameters set to (qint32_MIN, qint32_MIN).
0067      * \param col column of the tile
0068      * \param row row of the tile
0069      * \param existingTile returns true if the tile actually exists in the table
0070      *                     and it is not a lazily created default wrapper tile
0071      */
0072     TileTypeSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile);
0073     void addTile(TileTypeSP tile);
0074     bool deleteTile(TileTypeSP tile);
0075     bool deleteTile(qint32 col, qint32 row);
0076 
0077     void clear();
0078 
0079     void setDefaultTileData(KisTileData *defaultTileData);
0080     KisTileData* defaultTileData() const;
0081     KisTileData* refAndFetchDefaultTileData() const;
0082 
0083     qint32 numTiles() {
0084         return m_numTiles;
0085     }
0086 
0087     void debugPrintInfo();
0088     void debugMaxListLength(qint32 &min, qint32 &max);
0089 
0090 private:
0091 
0092     TileTypeSP getTileMinefieldWalk(qint32 col, qint32 row, qint32 idx);
0093     TileTypeSP getTile(qint32 col, qint32 row, qint32 idx);
0094     void linkTile(TileTypeSP tile, qint32 idx);
0095     bool unlinkTile(qint32 col, qint32 row, qint32 idx);
0096 
0097     inline void setDefaultTileDataImp(KisTileData *defaultTileData);
0098     inline KisTileData* defaultTileDataImp() const;
0099 
0100     static inline quint32 calculateHash(qint32 col, qint32 row);
0101 
0102     inline qint32 debugChainLen(qint32 idx);
0103     void debugListLengthDistribution();
0104     void sanityChecksumCheck();
0105 private:
0106     template<class U, class LockerType> friend class KisTileHashTableIteratorTraits;
0107 
0108     static const qint32 TABLE_SIZE = 1024;
0109     TileTypeSP *m_hashTable;
0110     qint32 m_numTiles;
0111 
0112     KisTileData *m_defaultTileData;
0113     KisMementoManager *m_mementoManager;
0114 
0115     mutable QReadWriteLock m_lock;
0116 };
0117 
0118 #include "kis_tile_hash_table_p.h"
0119 
0120 
0121 
0122 /**
0123  * Walks through all tiles inside hash table
0124  * Note: You can't work with your hash table in a regular way
0125  *       during iterating with this iterator, because HT is locked.
0126  *       The only thing you can do is to delete current tile.
0127  *
0128  * LockerType defines if the iterator is constant or mutable. One should
0129  * pass either QReadLocker or QWriteLocker as a parameter.
0130  */
0131 template<class T, class LockerType>
0132 class KisTileHashTableIteratorTraits
0133 {
0134 public:
0135     typedef T               TileType;
0136     typedef KisSharedPtr<T> TileTypeSP;
0137 
0138     KisTileHashTableIteratorTraits(KisTileHashTableTraits<T> *ht)
0139         : m_locker(&ht->m_lock)
0140     {
0141         m_hashTable = ht;
0142         m_index = nextNonEmptyList(0);
0143         if (m_index < KisTileHashTableTraits<T>::TABLE_SIZE)
0144             m_tile = m_hashTable->m_hashTable[m_index];
0145     }
0146 
0147     ~KisTileHashTableIteratorTraits() {
0148     }
0149 
0150     void next() {
0151         if (m_tile) {
0152             m_tile = m_tile->next();
0153             if (!m_tile) {
0154                 qint32 idx = nextNonEmptyList(m_index + 1);
0155                 if (idx < KisTileHashTableTraits<T>::TABLE_SIZE) {
0156                     m_index = idx;
0157                     m_tile = m_hashTable->m_hashTable[idx];
0158                 } else {
0159                     //EOList reached
0160                     m_index = -1;
0161                     // m_tile.clear(); // already null
0162                 }
0163             }
0164         }
0165     }
0166 
0167     TileTypeSP tile() const {
0168         return m_tile;
0169     }
0170     bool isDone() const {
0171         return !m_tile;
0172     }
0173 
0174     // disable the method if we didn't lock for writing
0175     template <class Helper = LockerType>
0176     typename std::enable_if<std::is_same<Helper, QWriteLocker>::value, void>::type
0177     deleteCurrent() {
0178         TileTypeSP tile = m_tile;
0179         next();
0180 
0181         const qint32 idx = m_hashTable->calculateHash(tile->col(), tile->row());
0182         m_hashTable->unlinkTile(tile->col(), tile->row(), idx);
0183     }
0184 
0185     // disable the method if we didn't lock for writing
0186     template <class Helper = LockerType>
0187     typename std::enable_if<std::is_same<Helper, QWriteLocker>::value, void>::type
0188     moveCurrentToHashTable(KisTileHashTableTraits<T> *newHashTable) {
0189         TileTypeSP tile = m_tile;
0190         next();
0191 
0192         const qint32 idx = m_hashTable->calculateHash(tile->col(), tile->row());
0193         m_hashTable->unlinkTile(tile->col(), tile->row(), idx);
0194 
0195         newHashTable->addTile(tile);
0196     }
0197 
0198 protected:
0199     TileTypeSP m_tile;
0200     qint32 m_index;
0201     KisTileHashTableTraits<T> *m_hashTable;
0202     LockerType m_locker;
0203 
0204 protected:
0205     qint32 nextNonEmptyList(qint32 startIdx) {
0206         qint32 idx = startIdx;
0207 
0208         while (idx < KisTileHashTableTraits<T>::TABLE_SIZE &&
0209                 !m_hashTable->m_hashTable[idx]) {
0210             idx++;
0211         }
0212 
0213         return idx;
0214     }
0215 private:
0216     Q_DISABLE_COPY(KisTileHashTableIteratorTraits)
0217 };
0218 
0219 
0220 typedef KisTileHashTableTraits<KisTile> KisTileHashTable;
0221 typedef KisTileHashTableIteratorTraits<KisTile, QWriteLocker> KisTileHashTableIterator;
0222 typedef KisTileHashTableIteratorTraits<KisTile, QReadLocker> KisTileHashTableConstIterator;
0223 
0224 #endif /* KIS_TILEHASHTABLE_H_ */