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

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_debug.h"
0008 #include "kis_chunk_allocator.h"
0009 
0010 
0011 #define GAP_SIZE(low, high) ((high) - (low) > 0 ? (high) - (low) - 1 : 0)
0012 
0013 #define HAS_NEXT(list,iter) ((iter)!=(list).end())
0014 #define HAS_PREVIOUS(list,iter) ((iter)!=(list).begin())
0015 
0016 #define PEEK_NEXT(iter) (*(iter))
0017 #define PEEK_PREVIOUS(iter) (*((iter)-1))
0018 #define WRAP_PREVIOUS_CHUNK_DATA(iter) (KisChunk((iter)-1))
0019 
0020 
0021 KisChunkAllocator::KisChunkAllocator(quint64 slabSize, quint64 storeSize)
0022 {
0023     m_storeMaxSize = storeSize;
0024     m_storeSlabSize = slabSize;
0025 
0026     m_iterator = m_list.begin();
0027     m_storeSize = m_storeSlabSize;
0028     INIT_FAIL_COUNTER();
0029 }
0030 
0031 KisChunkAllocator::~KisChunkAllocator()
0032 {
0033 }
0034 
0035 KisChunk KisChunkAllocator::getChunk(quint64 size)
0036 {
0037     KisChunkDataListIterator startPosition = m_iterator;
0038     START_COUNTING();
0039 
0040     forever {
0041         if(tryInsertChunk(m_list, m_iterator, size))
0042             return WRAP_PREVIOUS_CHUNK_DATA(m_iterator);
0043 
0044         if(m_iterator == m_list.end())
0045             break;
0046 
0047         m_iterator++;
0048         REGISTER_STEP();
0049     }
0050 
0051     REGISTER_FAIL();
0052     m_iterator = m_list.begin();
0053 
0054     forever {
0055         if(tryInsertChunk(m_list, m_iterator, size))
0056             return WRAP_PREVIOUS_CHUNK_DATA(m_iterator);
0057 
0058         if(m_iterator == m_list.end() || m_iterator == startPosition)
0059             break;
0060 
0061         m_iterator++;
0062         REGISTER_STEP();
0063     }
0064 
0065     REGISTER_FAIL();
0066     m_iterator = m_list.end();
0067 
0068     while ((m_storeSize += m_storeSlabSize) <= m_storeMaxSize) {
0069         if(tryInsertChunk(m_list, m_iterator, size))
0070             return WRAP_PREVIOUS_CHUNK_DATA(m_iterator);
0071     }
0072 
0073     qFatal("KisChunkAllocator: out of swap space");
0074 
0075     // just let gcc be happy! :)
0076     return KisChunk(m_list.end());
0077 }
0078 
0079 bool KisChunkAllocator::tryInsertChunk(KisChunkDataList &list,
0080                                        KisChunkDataListIterator &iterator,
0081                                        quint64 size)
0082 {
0083     bool result = false;
0084     quint64 highBound = m_storeSize;
0085     quint64 lowBound = 0;
0086     quint64 shift = 0;
0087 
0088     if(HAS_NEXT(list, iterator))
0089         highBound = PEEK_NEXT(iterator).m_begin;
0090 
0091     if(HAS_PREVIOUS(list, iterator)) {
0092         lowBound = PEEK_PREVIOUS(iterator).m_end;
0093         shift = 1;
0094     }
0095 
0096     if(GAP_SIZE(lowBound, highBound) >= size) {
0097         list.insert(iterator, KisChunkData(lowBound + shift, size));
0098         result = true;
0099     }
0100 
0101     return result;
0102 }
0103 
0104 void KisChunkAllocator::freeChunk(KisChunk chunk)
0105 {
0106     if(m_iterator != m_list.end() && m_iterator == chunk.position()) {
0107         m_iterator = m_list.erase(m_iterator);
0108         return;
0109     }
0110 
0111     Q_ASSERT(chunk.position()->m_begin == chunk.begin());
0112     m_list.erase(chunk.position());
0113 }
0114 
0115 
0116 
0117 /**************************************************************/
0118 /*******             Debugging features                ********/
0119 /**************************************************************/
0120 
0121 
0122 void KisChunkAllocator::debugChunks()
0123 {
0124     quint64 idx = 0;
0125     KisChunkDataListIterator i;
0126 
0127     for(i = m_list.begin(); i != m_list.end(); ++i) {
0128         qInfo("chunk #%lld: [%lld %lld]", idx++, i->m_begin, i->m_end);
0129     }
0130 }
0131 
0132 bool KisChunkAllocator::sanityCheck(bool pleaseCrash)
0133 {
0134     bool failed = false;
0135     KisChunkDataListIterator i;
0136 
0137     for(i = m_list.begin(); i != m_list.end(); ++i) {
0138         if(HAS_PREVIOUS(m_list, i)) {
0139             if(PEEK_PREVIOUS(i).m_end >= i->m_begin) {
0140                 qWarning("Chunks overlapped: [%lld %lld], [%lld %lld]", PEEK_PREVIOUS(i).m_begin, PEEK_PREVIOUS(i).m_end, i->m_begin, i->m_end);
0141                 failed = true;
0142                 break;
0143             }
0144         }
0145     }
0146 
0147     i = m_list.end();
0148     if(HAS_PREVIOUS(m_list, i)) {
0149         if(PEEK_PREVIOUS(i).m_end >= m_storeSize) {
0150             warnKrita << "Last chunk exceeds the store size!";
0151             failed = true;
0152         }
0153     }
0154 
0155     if(failed && pleaseCrash)
0156         qFatal("KisChunkAllocator: sanity check failed!");
0157 
0158     return !failed;
0159 }
0160 
0161 qreal KisChunkAllocator::debugFragmentation(bool toStderr)
0162 {
0163     KisChunkDataListIterator i;
0164 
0165     quint64 totalSize = 0;
0166     quint64 allocated = 0;
0167     quint64 free = 0;
0168     qreal fragmentation = 0;
0169 
0170     for(i = m_list.begin(); i != m_list.end(); ++i) {
0171         allocated += i->m_end - i->m_begin + 1;
0172 
0173         if(HAS_PREVIOUS(m_list, i))
0174             free += GAP_SIZE(PEEK_PREVIOUS(i).m_end, i->m_begin);
0175         else
0176             free += i->m_begin;
0177     }
0178 
0179     i = m_list.end();
0180     if(HAS_PREVIOUS(m_list, i))
0181         totalSize = PEEK_PREVIOUS(i).m_end + 1;
0182 
0183     if(totalSize)
0184         fragmentation = qreal(free) / totalSize;
0185 
0186     if(toStderr) {
0187         qInfo() << "Hard store limit:\t" << m_storeMaxSize;
0188         qInfo() << "Slab size:\t\t" << m_storeSlabSize;
0189         qInfo() << "Num slabs:\t\t" << m_storeSize / m_storeSlabSize;
0190         qInfo() << "Store size:\t\t" << m_storeSize;
0191         qInfo() << "Total used:\t\t" << totalSize;
0192         qInfo() << "Allocated:\t\t" << allocated;
0193         qInfo() << "Free:\t\t\t" << free;
0194         qInfo() << "Fragmentation:\t\t" << fragmentation;
0195         DEBUG_FAIL_COUNTER();
0196     }
0197 
0198     Q_ASSERT(totalSize == allocated + free);
0199 
0200     return fragmentation;
0201 }