-
Notifications
You must be signed in to change notification settings - Fork 6
/
ObjectPool.h
167 lines (143 loc) · 3.41 KB
/
ObjectPool.h
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
164
165
166
167
#ifndef PFZ_OBJECT_POOL_H
#define PFZ_OBJECT_POOL_H
#include <stdlib.h>
#include <iostream>
class DefaultMemoryAllocator
{
public:
static inline void *Allocate(size_t size)
{
return ::operator new(size, ::std::nothrow);
}
static inline void Deallocate(void *pointer, size_t size)
{
::operator delete(pointer);
}
};
template<typename T, class TMemoryAllocator=DefaultMemoryAllocator>
class ObjectPool
{
private:
struct _Node
{
void *_memory;
size_t _capacity;
_Node *_nextNode;
_Node(size_t capacity)
{
if (capacity < 1)
throw std::invalid_argument("capacity must be at least 1.");
_memory = TMemoryAllocator::Allocate(_itemSize * capacity);
if (_memory == NULL)
throw std::bad_alloc();
_capacity = capacity;
_nextNode = NULL;
}
~_Node()
{
TMemoryAllocator::Deallocate(_memory, _itemSize * _capacity);
}
};
void *_nodeMemory;
T *_firstDeleted;
size_t _countInNode;
size_t _nodeCapacity;
_Node _firstNode;
_Node *_lastNode;
size_t _maxBlockLength;
static const size_t _itemSize;
ObjectPool(const ObjectPool<T, TMemoryAllocator> &source);
void operator = (const ObjectPool<T, TMemoryAllocator> &source);
void _AllocateNewNode()
{
size_t size = _countInNode;
if (size >= _maxBlockLength)
size = _maxBlockLength;
else
{
size *= 2;
if (size < _countInNode)
throw std::overflow_error("size became too big.");
if (size >= _maxBlockLength)
size = _maxBlockLength;
}
_Node *newNode = new _Node(size);
_lastNode->_nextNode = newNode;
_lastNode = newNode;
_nodeMemory = newNode->_memory;
_countInNode = 0;
_nodeCapacity = size;
}
public:
explicit ObjectPool(size_t initialCapacity=32, size_t maxBlockLength=1000000):
_firstDeleted(NULL),
_countInNode(0),
_nodeCapacity(initialCapacity),
_firstNode(initialCapacity),
_maxBlockLength(maxBlockLength)
{
if (maxBlockLength < 1)
throw std::invalid_argument("maxBlockLength must be at least 1.");
_nodeMemory = _firstNode._memory;
_lastNode = &_firstNode;
}
~ObjectPool()
{
_Node *node = _firstNode._nextNode;
while(node)
{
_Node *nextNode = node->_nextNode;
delete node;
node = nextNode;
}
}
T *New()
{
if (_firstDeleted)
{
T *result = _firstDeleted;
_firstDeleted = *((T **)_firstDeleted);
new(result) T();
return result;
}
if (_countInNode >= _nodeCapacity)
_AllocateNewNode();
char *address = (char *)_nodeMemory;
address += _countInNode * _itemSize;
T *result = new(address) T();
_countInNode++;
return result;
}
// This method is useful if you want to call a non-default constructor.
// It should be used like this:
// new (pool.GetNextWithoutInitializing()) ObjectType(... parameters ...);
T *GetNextWithoutInitializing()
{
if (_firstDeleted)
{
T *result = (T *)_firstDeleted;
_firstDeleted = *((T **)_firstDeleted);
return result;
}
if (_countInNode >= _nodeCapacity)
_AllocateNewNode();
char *address = (char *)_nodeMemory;
address += _countInNode * _itemSize;
_countInNode++;
return (T *)address;
}
void Delete(T *content)
{
content->~T();
*((T **)content) = _firstDeleted;
_firstDeleted = content;
}
void DeleteWithoutDestroying(T *content)
{
*((T **)content) = _firstDeleted;
_firstDeleted = content;
}
};
template<typename T, class TMemoryAllocator>
const size_t ObjectPool<T, TMemoryAllocator>::_itemSize = ((sizeof(T) + sizeof(void *)-1) / sizeof(void *)) * sizeof(void *);
#endif