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 }