-
Notifications
You must be signed in to change notification settings - Fork 41
/
xallocator.cpp
417 lines (354 loc) · 12.7 KB
/
xallocator.cpp
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#include "xallocator.h"
#include "Allocator.h"
#include "Fault.h"
#include <iostream>
#include <string.h>
using namespace std;
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
#if WIN32
// @TODO Create a lock for non-Windows platforms when using an operating system
static CRITICAL_SECTION _criticalSection;
#endif
static BOOL _xallocInitialized = FALSE;
// Define STATIC_POOLS to switch from heap blocks mode to static pools mode
//#define STATIC_POOLS
#ifdef STATIC_POOLS
// Update this section as necessary if you want to use static memory pools.
// See also xalloc_init() and xalloc_destroy() for additional updates required.
#define MAX_ALLOCATORS 12
#define MAX_BLOCKS 32
// Create static storage for each static allocator instance
CHAR* _allocator8 [sizeof(AllocatorPool<CHAR[8], MAX_BLOCKS>)];
CHAR* _allocator16 [sizeof(AllocatorPool<CHAR[16], MAX_BLOCKS>)];
CHAR* _allocator32 [sizeof(AllocatorPool<CHAR[32], MAX_BLOCKS>)];
CHAR* _allocator64 [sizeof(AllocatorPool<CHAR[64], MAX_BLOCKS>)];
CHAR* _allocator128 [sizeof(AllocatorPool<CHAR[128], MAX_BLOCKS>)];
CHAR* _allocator256 [sizeof(AllocatorPool<CHAR[256], MAX_BLOCKS>)];
CHAR* _allocator396 [sizeof(AllocatorPool<CHAR[396], MAX_BLOCKS>)];
CHAR* _allocator512 [sizeof(AllocatorPool<CHAR[512], MAX_BLOCKS>)];
CHAR* _allocator768 [sizeof(AllocatorPool<CHAR[768], MAX_BLOCKS>)];
CHAR* _allocator1024 [sizeof(AllocatorPool<CHAR[1024], MAX_BLOCKS>)];
CHAR* _allocator2048 [sizeof(AllocatorPool<CHAR[2048], MAX_BLOCKS>)];
CHAR* _allocator4096 [sizeof(AllocatorPool<CHAR[4096], MAX_BLOCKS>)];
// Array of pointers to all allocator instances
static Allocator* _allocators[MAX_ALLOCATORS];
#else
#define MAX_ALLOCATORS 15
static Allocator* _allocators[MAX_ALLOCATORS];
#endif // STATIC_POOLS
// For C++ applications, must define AUTOMATIC_XALLOCATOR_INIT_DESTROY to
// correctly ensure allocators are initialized before any static user C++
// construtor/destructor executes which might call into the xallocator API.
// This feature costs 1-byte of RAM per C++ translation unit. This feature
// can be disabled only under the following circumstances:
//
// 1) The xallocator is only used within C files.
// 2) STATIC_POOLS is undefined and the application never exits main (e.g.
// an embedded system).
//
// In either of the two cases above, call xalloc_init() in main at startup,
// and xalloc_destroy() before main exits. In all other situations
// XallocInitDestroy must be used to call xalloc_init() and xalloc_destroy().
#ifdef AUTOMATIC_XALLOCATOR_INIT_DESTROY
INT XallocInitDestroy::refCount = 0;
XallocInitDestroy::XallocInitDestroy()
{
// Track how many static instances of XallocInitDestroy are created
if (refCount++ == 0)
xalloc_init();
}
XallocInitDestroy::~XallocInitDestroy()
{
// Last static instance to have destructor called?
if (--refCount == 0)
xalloc_destroy();
}
#endif // AUTOMATIC_XALLOCATOR_INIT_DESTROY
/// Returns the next higher powers of two. For instance, pass in 12 and
/// the value returned would be 16.
/// @param[in] k - numeric value to compute the next higher power of two.
/// @return The next higher power of two based on the input k.
template <class T>
T nexthigher(T k)
{
k--;
for (size_t i=1; i<sizeof(T)*CHAR_BIT; i<<=1)
k |= (k >> i);
return k+1;
}
/// Create the xallocator lock. Call only one time at startup.
static void lock_init()
{
#if WIN32
BOOL success = InitializeCriticalSectionAndSpinCount(&_criticalSection, 0x00000400);
ASSERT_TRUE(success != 0);
#endif
_xallocInitialized = TRUE;
}
/// Destroy the xallocator lock.
static void lock_destroy()
{
#if WIN32
DeleteCriticalSection(&_criticalSection);
#endif
_xallocInitialized = FALSE;
}
/// Lock the shared resource.
static inline void lock_get()
{
if (_xallocInitialized == FALSE)
return;
#if WIN32
EnterCriticalSection(&_criticalSection);
#endif
}
/// Unlock the shared resource.
static inline void lock_release()
{
if (_xallocInitialized == FALSE)
return;
#if WIN32
LeaveCriticalSection(&_criticalSection);
#endif
}
/// Stored a pointer to the allocator instance within the block region.
/// a pointer to the client's area within the block.
/// @param[in] block - a pointer to the raw memory block.
/// @param[in] size - the client requested size of the memory block.
/// @return A pointer to the client's address within the raw memory block.
static inline void *set_block_allocator(void* block, Allocator* allocator)
{
// Cast the raw block memory to a Allocator pointer
Allocator** pAllocatorInBlock = static_cast<Allocator**>(block);
// Write the size into the memory block
*pAllocatorInBlock = allocator;
// Advance the pointer past the Allocator* block size and return a pointer to
// the client's memory region
return ++pAllocatorInBlock;
}
/// Gets the size of the memory block stored within the block.
/// @param[in] block - a pointer to the client's memory block.
/// @return The original allocator instance stored in the memory block.
static inline Allocator* get_block_allocator(void* block)
{
// Cast the client memory to a Allocator pointer
Allocator** pAllocatorInBlock = static_cast<Allocator**>(block);
// Back up one Allocator* position to get the stored allocator instance
pAllocatorInBlock--;
// Return the allocator instance stored within the memory block
return *pAllocatorInBlock;
}
/// Returns the raw memory block pointer given a client memory pointer.
/// @param[in] block - a pointer to the client memory block.
/// @return A pointer to the original raw memory block address.
static inline void *get_block_ptr(void* block)
{
// Cast the client memory to a Allocator* pointer
Allocator** pAllocatorInBlock = static_cast<Allocator**>(block);
// Back up one Allocator* position and return the original raw memory block pointer
return --pAllocatorInBlock;
}
/// Returns an allocator instance matching the size provided
/// @param[in] size - allocator block size
/// @return Allocator instance handling requested block size or NULL
/// if no allocator exists.
static inline Allocator* find_allocator(size_t size)
{
for (INT i=0; i<MAX_ALLOCATORS; i++)
{
if (_allocators[i] == 0)
break;
if (_allocators[i]->GetBlockSize() == size)
return _allocators[i];
}
return NULL;
}
/// Insert an allocator instance into the array
/// @param[in] allocator - An allocator instance
static inline void insert_allocator(Allocator* allocator)
{
for (INT i=0; i<MAX_ALLOCATORS; i++)
{
if (_allocators[i] == 0)
{
_allocators[i] = allocator;
return;
}
}
ASSERT();
}
/// This function must be called exactly one time *before* any other xallocator
/// API is called. XallocInitDestroy constructor calls this function automatically.
extern "C" void xalloc_init()
{
lock_init();
#ifdef STATIC_POOLS
// For STATIC_POOLS mode, the allocators must be initialized before any other
// static user class constructor is run. Therefore, use placement new to initialize
// each allocator into the previously reserved static memory locations.
new (&_allocator8) AllocatorPool<CHAR[8], MAX_BLOCKS>();
new (&_allocator16) AllocatorPool<CHAR[16], MAX_BLOCKS>();
new (&_allocator32) AllocatorPool<CHAR[32], MAX_BLOCKS>();
new (&_allocator64) AllocatorPool<CHAR[64], MAX_BLOCKS>();
new (&_allocator128) AllocatorPool<CHAR[128], MAX_BLOCKS>();
new (&_allocator256) AllocatorPool<CHAR[256], MAX_BLOCKS>();
new (&_allocator396) AllocatorPool<CHAR[396], MAX_BLOCKS>();
new (&_allocator512) AllocatorPool<CHAR[512], MAX_BLOCKS>();
new (&_allocator768) AllocatorPool<CHAR[768], MAX_BLOCKS>();
new (&_allocator1024) AllocatorPool<CHAR[1024], MAX_BLOCKS>();
new (&_allocator2048) AllocatorPool<CHAR[2048], MAX_BLOCKS>();
new (&_allocator4096) AllocatorPool<CHAR[4096], MAX_BLOCKS>();
// Populate allocator array with all instances
_allocators[0] = (Allocator*)&_allocator8;
_allocators[1] = (Allocator*)&_allocator16;
_allocators[2] = (Allocator*)&_allocator32;
_allocators[3] = (Allocator*)&_allocator64;
_allocators[4] = (Allocator*)&_allocator128;
_allocators[5] = (Allocator*)&_allocator256;
_allocators[6] = (Allocator*)&_allocator396;
_allocators[7] = (Allocator*)&_allocator512;
_allocators[8] = (Allocator*)&_allocator768;
_allocators[9] = (Allocator*)&_allocator1024;
_allocators[10] = (Allocator*)&_allocator2048;
_allocators[11] = (Allocator*)&_allocator4096;
#endif
}
/// Called one time when the application exits to cleanup any allocated memory.
/// ~XallocInitDestroy destructor calls this function automatically.
extern "C" void xalloc_destroy()
{
lock_get();
#ifdef STATIC_POOLS
for (INT i=0; i<MAX_ALLOCATORS; i++)
{
_allocators[i]->~Allocator();
_allocators[i] = 0;
}
#else
for (INT i=0; i<MAX_ALLOCATORS; i++)
{
if (_allocators[i] == 0)
break;
delete _allocators[i];
_allocators[i] = 0;
}
#endif
lock_release();
lock_destroy();
}
/// Get an Allocator instance based upon the client's requested block size.
/// If a Allocator instance is not currently available to handle the size,
/// then a new Allocator instance is create.
/// @param[in] size - the client's requested block size.
/// @return An Allocator instance that handles blocks of the requested
/// size.
extern "C" Allocator* xallocator_get_allocator(size_t size)
{
// Based on the size, find the next higher powers of two value.
// Add sizeof(Allocator*) to the requested block size to hold the size
// within the block memory region. Most blocks are powers of two,
// however some common allocator block sizes can be explicitly defined
// to minimize wasted storage. This offers application specific tuning.
size_t blockSize = size + sizeof(Allocator*);
if (blockSize > 256 && blockSize <= 396)
blockSize = 396;
else if (blockSize > 512 && blockSize <= 768)
blockSize = 768;
else
blockSize = nexthigher<size_t>(blockSize);
Allocator* allocator = find_allocator(blockSize);
#ifdef STATIC_POOLS
ASSERT_TRUE(allocator != NULL);
#else
// If there is not an allocator already created to handle this block size
if (allocator == NULL)
{
// Create a new allocator to handle blocks of the size required
allocator = new Allocator(blockSize, 0, 0, "xallocator");
// Insert allocator into array
insert_allocator(allocator);
}
#endif
return allocator;
}
/// Allocates a memory block of the requested size. The blocks are created from
/// the fixed block allocators.
/// @param[in] size - the client requested size of the block.
/// @return A pointer to the client's memory block.
extern "C" void *xmalloc(size_t size)
{
lock_get();
// Allocate a raw memory block
Allocator* allocator = xallocator_get_allocator(size);
void* blockMemoryPtr = allocator->Allocate(sizeof(Allocator*) + size);
lock_release();
// Set the block Allocator* within the raw memory block region
void* clientsMemoryPtr = set_block_allocator(blockMemoryPtr, allocator);
return clientsMemoryPtr;
}
/// Frees a memory block previously allocated with xalloc. The blocks are returned
/// to the fixed block allocator that originally created it.
/// @param[in] ptr - a pointer to a block created with xalloc.
extern "C" void xfree(void* ptr)
{
if (ptr == 0)
return;
// Extract the original allocator instance from the caller's block pointer
Allocator* allocator = get_block_allocator(ptr);
// Convert the client pointer into the original raw block pointer
void* blockPtr = get_block_ptr(ptr);
lock_get();
// Deallocate the block
allocator->Deallocate(blockPtr);
lock_release();
}
/// Reallocates a memory block previously allocated with xalloc.
/// @param[in] ptr - a pointer to a block created with xalloc.
/// @param[in] size - the client requested block size to create.
extern "C" void *xrealloc(void *oldMem, size_t size)
{
if (oldMem == 0)
return xmalloc(size);
if (size == 0)
{
xfree(oldMem);
return 0;
}
else
{
// Create a new memory block
void* newMem = xmalloc(size);
if (newMem != 0)
{
// Get the original allocator instance from the old memory block
Allocator* oldAllocator = get_block_allocator(oldMem);
size_t oldSize = oldAllocator->GetBlockSize() - sizeof(Allocator*);
// Copy the bytes from the old memory block into the new (as much as will fit)
memcpy(newMem, oldMem, (oldSize < size) ? oldSize : size);
// Free the old memory block
xfree(oldMem);
// Return the client pointer to the new memory block
return newMem;
}
return 0;
}
}
/// Output xallocator usage statistics
extern "C" void xalloc_stats()
{
lock_get();
for (INT i=0; i<MAX_ALLOCATORS; i++)
{
if (_allocators[i] == 0)
break;
if (_allocators[i]->GetName() != NULL)
cout << _allocators[i]->GetName();
cout << " Block Size: " << _allocators[i]->GetBlockSize();
cout << " Block Count: " << _allocators[i]->GetBlockCount();
cout << " Blocks In Use: " << _allocators[i]->GetBlocksInUse();
cout << endl;
}
lock_release();
}