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