File indexing completed on 2024-05-12 15:58:51
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisOptimizedByteArray.h" 0008 0009 #include <QGlobalStatic> 0010 #include <QMutexLocker> 0011 0012 #include <string.h> 0013 0014 namespace { 0015 0016 /*****************************************************************/ 0017 /* DefaultMemoryAllocator */ 0018 /*****************************************************************/ 0019 0020 struct DefaultMemoryAllocator : KisOptimizedByteArray::MemoryAllocator 0021 { 0022 KisOptimizedByteArray::MemoryChunk alloc(int size) override { 0023 return KisOptimizedByteArray::MemoryChunk(new quint8[size], size); 0024 } 0025 0026 void free(KisOptimizedByteArray::MemoryChunk chunk) override { 0027 // chunk.first might be null 0028 delete[] chunk.first; 0029 } 0030 }; 0031 0032 0033 /*****************************************************************/ 0034 /* DefaultMemoryAllocatorStore */ 0035 /*****************************************************************/ 0036 0037 struct DefaultMemoryAllocatorStore { 0038 static DefaultMemoryAllocatorStore* instance(); 0039 0040 DefaultMemoryAllocatorStore() 0041 : m_allocator(new DefaultMemoryAllocator()) 0042 { 0043 } 0044 0045 inline KisOptimizedByteArray::MemoryAllocatorSP allocator() const { 0046 return m_allocator; 0047 } 0048 0049 private: 0050 KisOptimizedByteArray::MemoryAllocatorSP m_allocator; 0051 }; 0052 0053 Q_GLOBAL_STATIC(DefaultMemoryAllocatorStore, s_instance) 0054 0055 DefaultMemoryAllocatorStore *DefaultMemoryAllocatorStore::instance() 0056 { 0057 return s_instance; 0058 } 0059 0060 } // namespace 0061 0062 0063 /*****************************************************************/ 0064 /* KisOptimizedByteArray::PooledMemoryAllocator */ 0065 /*****************************************************************/ 0066 0067 KisOptimizedByteArray::PooledMemoryAllocator::PooledMemoryAllocator() 0068 : m_meanSize(500) 0069 { 0070 } 0071 0072 KisOptimizedByteArray::PooledMemoryAllocator::~PooledMemoryAllocator() 0073 { 0074 Q_FOREACH (const MemoryChunk &chunk, m_chunks) { 0075 delete[] chunk.first; 0076 } 0077 } 0078 0079 KisOptimizedByteArray::MemoryChunk 0080 KisOptimizedByteArray::PooledMemoryAllocator::alloc(int size) 0081 { 0082 MemoryChunk chunk; 0083 0084 { 0085 QMutexLocker l(&m_mutex); 0086 if (!m_chunks.isEmpty()) { 0087 chunk = m_chunks.takeLast(); 0088 } 0089 0090 m_meanSize(size); 0091 } 0092 0093 if (chunk.second < size) { 0094 delete[] chunk.first; 0095 0096 // we alloc a bit more memory for the dabs to let the chunks 0097 // be more reusable 0098 const int allocSize = 1.2 * size; 0099 chunk = KisOptimizedByteArray::MemoryChunk(new quint8[allocSize], allocSize); 0100 } 0101 0102 return chunk; 0103 } 0104 0105 void KisOptimizedByteArray::PooledMemoryAllocator::free(KisOptimizedByteArray::MemoryChunk chunk) 0106 { 0107 if (chunk.first) { 0108 QMutexLocker l(&m_mutex); 0109 0110 // keep bigger chunks for ourselves and return the 0111 // smaller ones to the system 0112 if (chunk.second > 0.8 * m_meanSize.rollingMean()) { 0113 m_chunks.append(chunk); 0114 } else { 0115 delete[] chunk.first; 0116 } 0117 } 0118 } 0119 0120 0121 /*****************************************************************/ 0122 /* KisOptimizedByteArray::Private */ 0123 /*****************************************************************/ 0124 0125 struct KisOptimizedByteArray::Private : public QSharedData 0126 { 0127 Private(MemoryAllocatorSP _allocator) 0128 { 0129 storedAllocator = 0130 _allocator ? _allocator : DefaultMemoryAllocatorStore::instance()->allocator(); 0131 0132 allocator = storedAllocator.data(); 0133 } 0134 0135 Private(const Private &rhs) 0136 : QSharedData(rhs) 0137 { 0138 allocator = rhs.allocator; 0139 storedAllocator = rhs.storedAllocator; 0140 dataSize = rhs.dataSize; 0141 if (dataSize) { 0142 data = allocator->alloc(dataSize); 0143 memcpy(data.first, rhs.data.first, dataSize); 0144 } 0145 } 0146 0147 ~Private() { 0148 allocator->free(data); 0149 } 0150 0151 MemoryAllocator *allocator; 0152 0153 // stored allocator shared pointer is used only for keeping 0154 // the lifetime of the allocator until the detach of the last 0155 // allocated chunk 0156 MemoryAllocatorSP storedAllocator; 0157 0158 MemoryChunk data; 0159 int dataSize = 0; 0160 }; 0161 0162 0163 /*****************************************************************/ 0164 /* KisOptimizedByteArray */ 0165 /*****************************************************************/ 0166 0167 KisOptimizedByteArray::KisOptimizedByteArray(MemoryAllocatorSP allocator) 0168 : m_d(new Private(allocator)) 0169 { 0170 } 0171 0172 KisOptimizedByteArray::KisOptimizedByteArray(const KisOptimizedByteArray &rhs) 0173 : m_d(rhs.m_d) 0174 { 0175 } 0176 0177 KisOptimizedByteArray &KisOptimizedByteArray::operator=(const KisOptimizedByteArray &rhs) 0178 { 0179 m_d = rhs.m_d; 0180 return *this; 0181 } 0182 0183 KisOptimizedByteArray::~KisOptimizedByteArray() 0184 { 0185 } 0186 0187 quint8 *KisOptimizedByteArray::data() 0188 { 0189 return const_cast<Private*>(m_d.data())->data.first; 0190 } 0191 0192 const quint8 *KisOptimizedByteArray::constData() const 0193 { 0194 return const_cast<const Private*>(m_d.constData())->data.first; 0195 } 0196 0197 void KisOptimizedByteArray::resize(int size) 0198 { 0199 if (size == m_d->dataSize) return; 0200 0201 if (size > m_d->data.second) { 0202 m_d->allocator->free(m_d->data); 0203 m_d->data = m_d->allocator->alloc(size); 0204 } 0205 m_d->dataSize = size; 0206 } 0207 0208 void KisOptimizedByteArray::fill(quint8 value, int size) 0209 { 0210 resize(size); 0211 memset(m_d->data.first, value, m_d->dataSize); 0212 } 0213 0214 int KisOptimizedByteArray::size() const 0215 { 0216 return m_d->dataSize; 0217 } 0218 0219 bool KisOptimizedByteArray::isEmpty() const 0220 { 0221 return !m_d->dataSize; 0222 } 0223 0224 KisOptimizedByteArray::MemoryAllocatorSP KisOptimizedByteArray::customMemoryAllocator() const 0225 { 0226 return m_d->storedAllocator; 0227 } 0228