aboutsummaryrefslogtreecommitdiff
path: root/common/memorypool.h
blob: b84012232cb1b9742146743bfae1d1e8a04b5b7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* 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