/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef COMMON_MEMORYPOOL_H #define COMMON_MEMORYPOOL_H #include "common/scummsys.h" #include "common/array.h" namespace Common { /** * This class provides a pool of memory 'chunks' of identical size. * The size of a chunk is determined when creating the memory pool. * * Using a memory pool may yield better performance and memory usage * when allocating and deallocating many memory blocks of equal size. * E.g. the Common::String class uses a memory pool for the refCount * variables (each the size of an int) it allocates for each string * instance. */ class MemoryPool { protected: MemoryPool(const MemoryPool&); MemoryPool& operator=(const MemoryPool&); struct Page { void *start; size_t numChunks; }; const size_t _chunkSize; Array<Page> _pages; void *_next; size_t _chunksPerPage; void allocPage(); void addPageToPool(const Page &page); bool isPointerInPage(void *ptr, const Page &page); public: /** * Constructor for a memory pool with the given chunk size. * @param chunkSize the chunk size of this memory pool */ explicit MemoryPool(size_t chunkSize); ~MemoryPool(); /** * Allocate a new chunk from the memory pool. */ void *allocChunk(); /** * Return a chunk to the memory pool. The given pointer must have * been obtained from calling the allocChunk() method of the very * same MemoryPool instance. Passing any other pointer (e.g. to * a chunk from another MemoryPool, or a malloc'ed memory block) * will lead to undefined behavior and may result in a crash (if * you are lucky) or in silent data corruption. */ void freeChunk(void *ptr); /** * Perform garbage collection. The memory pool stores all the * chunks it manages in memory 'pages' obtained via the classic * memory allocation APIs (i.e. malloc/free). Ordinarily, once * a page has been allocated, it won't be released again during * the life time of the memory pool. The exception is when this * method is called. */ void freeUnusedPages(); /** * Return the chunk size used by this memory pool. */ size_t getChunkSize() const { return _chunkSize; } }; /** * This is a memory pool which already contains in itself some storage * space for a fixed number of chunks. Thus if the memory pool is only * lightly used, no malloc() calls have to be made at all. */ template<size_t CHUNK_SIZE, size_t NUM_INTERNAL_CHUNKS = 32> class FixedSizeMemoryPool : public MemoryPool { private: enum { REAL_CHUNK_SIZE = (CHUNK_SIZE + sizeof(void *) - 1) & (~(sizeof(void *) - 1)) }; byte _storage[NUM_INTERNAL_CHUNKS * REAL_CHUNK_SIZE]; public: FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) { assert(REAL_CHUNK_SIZE == _chunkSize); // Insert some static storage Page internalPage = { _storage, NUM_INTERNAL_CHUNKS }; addPageToPool(internalPage); } }; // Ensure NUM_INTERNAL_CHUNKS == 0 results in a compile error template<size_t CHUNK_SIZE> class FixedSizeMemoryPool<CHUNK_SIZE,0> : public MemoryPool { public: FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {} }; /** * A memory pool for C++ objects. */ template<class T, size_t NUM_INTERNAL_CHUNKS = 32> class ObjectPool : public FixedSizeMemoryPool<sizeof(T), NUM_INTERNAL_CHUNKS> { public: /** * Return the memory chunk used as storage for the given object back * to the pool, after calling its destructor. */ void deleteChunk(T *ptr) { ptr->~T(); this->freeChunk(ptr); } }; } // End of namespace Common /** * A custom placement new operator, using an arbitrary MemoryPool. * * This *should* work with all C++ implementations, but may not. * * For details on using placement new for custom allocators, see e.g. * <http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14> */ inline void *operator new(size_t nbytes, Common::MemoryPool &pool) { assert(nbytes <= pool.getChunkSize()); return pool.allocChunk(); } inline void operator delete(void *p, Common::MemoryPool &pool) { pool.freeChunk(p); } #endif