1 | /** |
---|
2 | @file System.cpp |
---|
3 | |
---|
4 | @maintainer Morgan McGuire, matrix@graphics3d.com |
---|
5 | |
---|
6 | Note: every routine must call init() first. |
---|
7 | |
---|
8 | There are two kinds of detection used in this file. At compile |
---|
9 | time, the _MSC_VER #define is used to determine whether x86 assembly |
---|
10 | can be used at all. At runtime, processor detection is used to |
---|
11 | determine if we can safely call the routines that use that assembly. |
---|
12 | |
---|
13 | @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm |
---|
14 | @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1 |
---|
15 | @cite Michael Herf http://www.stereopsis.com/memcpy.html |
---|
16 | |
---|
17 | @created 2003-01-25 |
---|
18 | @edited 2006-05-17 |
---|
19 | */ |
---|
20 | |
---|
21 | #include "G3D/platform.h" |
---|
22 | #include "G3D/System.h" |
---|
23 | #include "G3D/debug.h" |
---|
24 | #include "G3D/format.h" |
---|
25 | |
---|
26 | #ifdef G3D_WIN32 |
---|
27 | |
---|
28 | #include <conio.h> |
---|
29 | #include <sys/timeb.h> |
---|
30 | #include "G3D/RegistryUtil.h" |
---|
31 | |
---|
32 | #elif defined(G3D_LINUX) |
---|
33 | |
---|
34 | #include <stdlib.h> |
---|
35 | #include <stdio.h> |
---|
36 | #include <string.h> |
---|
37 | #include <errno.h> |
---|
38 | #include <sys/types.h> |
---|
39 | #include <sys/select.h> |
---|
40 | #include <termios.h> |
---|
41 | #include <unistd.h> |
---|
42 | #include <sys/ioctl.h> |
---|
43 | #include <sys/time.h> |
---|
44 | #include <pthread.h> |
---|
45 | |
---|
46 | // #include <assert.h> |
---|
47 | |
---|
48 | #elif defined(G3D_OSX) |
---|
49 | |
---|
50 | #include <stdlib.h> |
---|
51 | #include <stdio.h> |
---|
52 | #include <errno.h> |
---|
53 | #include <sys/types.h> |
---|
54 | #include <sys/sysctl.h> |
---|
55 | #include <sys/select.h> |
---|
56 | #include <sys/time.h> |
---|
57 | #include <termios.h> |
---|
58 | #include <unistd.h> |
---|
59 | #include <pthread.h> |
---|
60 | #include <mach-o/arch.h> |
---|
61 | |
---|
62 | #include <sstream> |
---|
63 | #include <CoreServices/CoreServices.h> |
---|
64 | #endif |
---|
65 | |
---|
66 | #if defined(SSE) |
---|
67 | #include <xmmintrin.h> |
---|
68 | #endif |
---|
69 | |
---|
70 | namespace G3D { |
---|
71 | |
---|
72 | static char versionCstr[1024]; |
---|
73 | System::OutOfMemoryCallback System::outOfMemoryCallback = NULL; |
---|
74 | |
---|
75 | |
---|
76 | void System::init() { |
---|
77 | // Cannot use most G3D data structures or utility functions in here because |
---|
78 | // they are not initialized. |
---|
79 | |
---|
80 | static bool initialized = false; |
---|
81 | |
---|
82 | if (initialized) { |
---|
83 | return; |
---|
84 | } |
---|
85 | |
---|
86 | initialized = true; |
---|
87 | |
---|
88 | if ((G3D_VER % 100) != 0) { |
---|
89 | sprintf(versionCstr, "G3D %d.%02d beta %d", |
---|
90 | G3D_VER / 10000, |
---|
91 | (G3D_VER / 100) % 100, |
---|
92 | G3D_VER % 100); |
---|
93 | } else { |
---|
94 | sprintf(versionCstr, "G3D %d.%02d", |
---|
95 | G3D_VER / 10000, |
---|
96 | (G3D_VER / 100) % 100); |
---|
97 | } |
---|
98 | |
---|
99 | } |
---|
100 | |
---|
101 | |
---|
102 | |
---|
103 | void System::memcpy(void* dst, const void* src, size_t numBytes) { |
---|
104 | ::memcpy(dst, src, numBytes); |
---|
105 | } |
---|
106 | |
---|
107 | |
---|
108 | void System::memset(void* dst, uint8 value, size_t numBytes) { |
---|
109 | ::memset(dst, value, numBytes); |
---|
110 | } |
---|
111 | |
---|
112 | |
---|
113 | |
---|
114 | |
---|
115 | |
---|
116 | //////////////////////////////////////////////////////////////// |
---|
117 | class BufferPool { |
---|
118 | public: |
---|
119 | |
---|
120 | /** Only store buffers up to these sizes (in bytes) in each pool-> |
---|
121 | Different pools have different management strategies. |
---|
122 | |
---|
123 | A large block is preallocated for tiny buffers; they are used with |
---|
124 | tremendous frequency. Other buffers are allocated as demanded. |
---|
125 | */ |
---|
126 | enum {tinyBufferSize = 128, smallBufferSize = 1024, medBufferSize = 4096}; |
---|
127 | |
---|
128 | /** |
---|
129 | Most buffers we're allowed to store. |
---|
130 | 64000 * 128 = 8 MB (preallocated) |
---|
131 | 1024 * 1024 = 1 MB (allocated on demand) |
---|
132 | 1024 * 4096 = 4 MB (allocated on demand) |
---|
133 | */ |
---|
134 | enum {maxTinyBuffers = 64000, maxSmallBuffers = 1024, maxMedBuffers = 1024}; |
---|
135 | |
---|
136 | private: |
---|
137 | |
---|
138 | class MemBlock { |
---|
139 | public: |
---|
140 | void* ptr; |
---|
141 | size_t bytes; |
---|
142 | |
---|
143 | inline MemBlock() : ptr(NULL), bytes(0) {} |
---|
144 | inline MemBlock(void* p, size_t b) : ptr(p), bytes(b) {} |
---|
145 | }; |
---|
146 | |
---|
147 | MemBlock smallPool[maxSmallBuffers]; |
---|
148 | int smallPoolSize; |
---|
149 | |
---|
150 | MemBlock medPool[maxMedBuffers]; |
---|
151 | int medPoolSize; |
---|
152 | |
---|
153 | /** The tiny pool is a single block of storage into which all tiny |
---|
154 | objects are allocated. This provides better locality for |
---|
155 | small objects and avoids the search time, since all tiny |
---|
156 | blocks are exactly the same size. */ |
---|
157 | void* tinyPool[maxTinyBuffers]; |
---|
158 | int tinyPoolSize; |
---|
159 | |
---|
160 | /** Pointer to the data in the tiny pool */ |
---|
161 | void* tinyHeap; |
---|
162 | |
---|
163 | # ifdef G3D_WIN32 |
---|
164 | CRITICAL_SECTION mutex; |
---|
165 | # else |
---|
166 | pthread_mutex_t mutex; |
---|
167 | # endif |
---|
168 | |
---|
169 | /** Provide synchronization between threads */ |
---|
170 | void lock() { |
---|
171 | # ifdef G3D_WIN32 |
---|
172 | EnterCriticalSection(&mutex); |
---|
173 | # else |
---|
174 | pthread_mutex_lock(&mutex); |
---|
175 | # endif |
---|
176 | } |
---|
177 | |
---|
178 | void unlock() { |
---|
179 | # ifdef G3D_WIN32 |
---|
180 | LeaveCriticalSection(&mutex); |
---|
181 | # else |
---|
182 | pthread_mutex_unlock(&mutex); |
---|
183 | # endif |
---|
184 | } |
---|
185 | |
---|
186 | /** |
---|
187 | Malloc out of the tiny heap. |
---|
188 | */ |
---|
189 | inline void* tinyMalloc(size_t bytes) { |
---|
190 | // Note that we ignore the actual byte size |
---|
191 | // and create a constant size block. |
---|
192 | (void)bytes; |
---|
193 | debugAssert(tinyBufferSize >= bytes); |
---|
194 | |
---|
195 | void* ptr = NULL; |
---|
196 | |
---|
197 | if (tinyPoolSize > 0) { |
---|
198 | --tinyPoolSize; |
---|
199 | // Return the last one |
---|
200 | ptr = tinyPool[tinyPoolSize]; |
---|
201 | } |
---|
202 | |
---|
203 | return ptr; |
---|
204 | } |
---|
205 | |
---|
206 | /** Returns true if this is a pointer into the tiny heap. */ |
---|
207 | bool inTinyHeap(void* ptr) { |
---|
208 | return (ptr >= tinyHeap) && |
---|
209 | (ptr < (uint8*)tinyHeap + maxTinyBuffers * tinyBufferSize); |
---|
210 | } |
---|
211 | |
---|
212 | void tinyFree(void* ptr) { |
---|
213 | debugAssert(tinyPoolSize < maxTinyBuffers); |
---|
214 | |
---|
215 | // Put the pointer back into the free list |
---|
216 | tinyPool[tinyPoolSize] = ptr; |
---|
217 | ++tinyPoolSize; |
---|
218 | |
---|
219 | } |
---|
220 | |
---|
221 | void flushPool(MemBlock* pool, int& poolSize) { |
---|
222 | for (int i = 0; i < poolSize; ++i) { |
---|
223 | ::free(pool->ptr); |
---|
224 | pool->ptr = NULL; |
---|
225 | pool->bytes = 0; |
---|
226 | } |
---|
227 | poolSize = 0; |
---|
228 | } |
---|
229 | |
---|
230 | |
---|
231 | /** Allocate out of a specific pool-> Return NULL if no suitable |
---|
232 | memory was found. |
---|
233 | |
---|
234 | */ |
---|
235 | void* malloc(MemBlock* pool, int& poolSize, size_t bytes) { |
---|
236 | |
---|
237 | // OPT: find the smallest block that satisfies the request. |
---|
238 | |
---|
239 | // See if there's something we can use in the buffer pool-> |
---|
240 | // Search backwards since usually we'll re-use the last one. |
---|
241 | for (int i = (int)poolSize - 1; i >= 0; --i) { |
---|
242 | if (pool[i].bytes >= bytes) { |
---|
243 | // We found a suitable entry in the pool-> |
---|
244 | |
---|
245 | // No need to offset the pointer; it is already offset |
---|
246 | void* ptr = pool[i].ptr; |
---|
247 | |
---|
248 | // Remove this element from the pool |
---|
249 | --poolSize; |
---|
250 | pool[i] = pool[poolSize]; |
---|
251 | |
---|
252 | return ptr; |
---|
253 | } |
---|
254 | } |
---|
255 | |
---|
256 | return NULL; |
---|
257 | } |
---|
258 | |
---|
259 | public: |
---|
260 | |
---|
261 | /** Count of memory allocations that have occurred. */ |
---|
262 | int totalMallocs; |
---|
263 | int mallocsFromTinyPool; |
---|
264 | int mallocsFromSmallPool; |
---|
265 | int mallocsFromMedPool; |
---|
266 | |
---|
267 | /** Amount of memory currently allocated (according to the application). |
---|
268 | This does not count the memory still remaining in the buffer pool, |
---|
269 | but does count extra memory required for rounding off to the size |
---|
270 | of a buffer. |
---|
271 | Primarily useful for detecting leaks.*/ |
---|
272 | // TODO: make me an atomic int! |
---|
273 | int bytesAllocated; |
---|
274 | |
---|
275 | BufferPool() { |
---|
276 | totalMallocs = 0; |
---|
277 | |
---|
278 | mallocsFromTinyPool = 0; |
---|
279 | mallocsFromSmallPool = 0; |
---|
280 | mallocsFromMedPool = 0; |
---|
281 | |
---|
282 | bytesAllocated = true; |
---|
283 | |
---|
284 | tinyPoolSize = 0; |
---|
285 | tinyHeap = NULL; |
---|
286 | |
---|
287 | smallPoolSize = 0; |
---|
288 | |
---|
289 | medPoolSize = 0; |
---|
290 | |
---|
291 | |
---|
292 | // Initialize the tiny heap as a bunch of pointers into one |
---|
293 | // pre-allocated buffer. |
---|
294 | tinyHeap = ::malloc(maxTinyBuffers * tinyBufferSize); |
---|
295 | for (int i = 0; i < maxTinyBuffers; ++i) { |
---|
296 | tinyPool[i] = (uint8*)tinyHeap + (tinyBufferSize * i); |
---|
297 | } |
---|
298 | tinyPoolSize = maxTinyBuffers; |
---|
299 | |
---|
300 | # ifdef G3D_WIN32 |
---|
301 | InitializeCriticalSection(&mutex); |
---|
302 | # else |
---|
303 | pthread_mutex_init(&mutex, NULL); |
---|
304 | # endif |
---|
305 | } |
---|
306 | |
---|
307 | |
---|
308 | ~BufferPool() { |
---|
309 | ::free(tinyHeap); |
---|
310 | # ifdef G3D_WIN32 |
---|
311 | DeleteCriticalSection(&mutex); |
---|
312 | # else |
---|
313 | // No destruction on pthreads |
---|
314 | # endif |
---|
315 | } |
---|
316 | |
---|
317 | |
---|
318 | void* realloc(void* ptr, size_t bytes) { |
---|
319 | if (ptr == NULL) { |
---|
320 | return malloc(bytes); |
---|
321 | } |
---|
322 | |
---|
323 | if (inTinyHeap(ptr)) { |
---|
324 | if (bytes <= tinyBufferSize) { |
---|
325 | // The old pointer actually had enough space. |
---|
326 | return ptr; |
---|
327 | } else { |
---|
328 | // Free the old pointer and malloc |
---|
329 | |
---|
330 | void* newPtr = malloc(bytes); |
---|
331 | System::memcpy(newPtr, ptr, tinyBufferSize); |
---|
332 | tinyFree(ptr); |
---|
333 | return newPtr; |
---|
334 | |
---|
335 | } |
---|
336 | } else { |
---|
337 | // In one of our heaps. |
---|
338 | |
---|
339 | // See how big the block really was |
---|
340 | size_t realSize = ((uint32*)ptr)[-1]; |
---|
341 | if (bytes <= realSize) { |
---|
342 | // The old block was big enough. |
---|
343 | return ptr; |
---|
344 | } |
---|
345 | |
---|
346 | // Need to reallocate |
---|
347 | void* newPtr = malloc(bytes); |
---|
348 | System::memcpy(newPtr, ptr, realSize); |
---|
349 | free(ptr); |
---|
350 | return newPtr; |
---|
351 | } |
---|
352 | } |
---|
353 | |
---|
354 | |
---|
355 | void* malloc(size_t bytes) { |
---|
356 | lock(); |
---|
357 | ++totalMallocs; |
---|
358 | |
---|
359 | if (bytes <= tinyBufferSize) { |
---|
360 | |
---|
361 | void* ptr = tinyMalloc(bytes); |
---|
362 | |
---|
363 | if (ptr) { |
---|
364 | ++mallocsFromTinyPool; |
---|
365 | unlock(); |
---|
366 | return ptr; |
---|
367 | } |
---|
368 | |
---|
369 | } |
---|
370 | |
---|
371 | // Failure to allocate a tiny buffer is allowed to flow |
---|
372 | // through to a small buffer |
---|
373 | if (bytes <= smallBufferSize) { |
---|
374 | |
---|
375 | void* ptr = malloc(smallPool, smallPoolSize, bytes); |
---|
376 | |
---|
377 | if (ptr) { |
---|
378 | ++mallocsFromSmallPool; |
---|
379 | unlock(); |
---|
380 | return ptr; |
---|
381 | } |
---|
382 | |
---|
383 | } else if (bytes <= medBufferSize) { |
---|
384 | // Note that a small allocation failure does *not* fall |
---|
385 | // through into a medium allocation because that would |
---|
386 | // waste the medium buffer's resources. |
---|
387 | |
---|
388 | void* ptr = malloc(medPool, medPoolSize, bytes); |
---|
389 | |
---|
390 | if (ptr) { |
---|
391 | ++mallocsFromMedPool; |
---|
392 | unlock(); |
---|
393 | return ptr; |
---|
394 | } |
---|
395 | } |
---|
396 | |
---|
397 | bytesAllocated += 4 + (int) bytes; |
---|
398 | unlock(); |
---|
399 | |
---|
400 | // Heap allocate |
---|
401 | |
---|
402 | // Allocate 4 extra bytes for our size header (unfortunate, |
---|
403 | // since malloc already added its own header). |
---|
404 | void* ptr = ::malloc(bytes + 4); |
---|
405 | |
---|
406 | if (ptr == NULL) { |
---|
407 | // Flush memory pools to try and recover space |
---|
408 | flushPool(smallPool, smallPoolSize); |
---|
409 | flushPool(medPool, medPoolSize); |
---|
410 | ptr = ::malloc(bytes + 4); |
---|
411 | } |
---|
412 | |
---|
413 | |
---|
414 | if (ptr == NULL) { |
---|
415 | if ((System::outOfMemoryCallback != NULL) && |
---|
416 | (System::outOfMemoryCallback(bytes + 4, true) == true)) { |
---|
417 | // Re-attempt the malloc |
---|
418 | ptr = ::malloc(bytes + 4); |
---|
419 | } |
---|
420 | } |
---|
421 | |
---|
422 | if (ptr == NULL) { |
---|
423 | if (System::outOfMemoryCallback != NULL) { |
---|
424 | // Notify the application |
---|
425 | System::outOfMemoryCallback(bytes + 4, false); |
---|
426 | } |
---|
427 | return NULL; |
---|
428 | } |
---|
429 | |
---|
430 | *(uint32*)ptr = (uint32)bytes; |
---|
431 | |
---|
432 | return (uint8*)ptr + 4; |
---|
433 | } |
---|
434 | |
---|
435 | |
---|
436 | void free(void* ptr) { |
---|
437 | if (ptr == NULL) { |
---|
438 | // Free does nothing on null pointers |
---|
439 | return; |
---|
440 | } |
---|
441 | |
---|
442 | debugAssert(isValidPointer(ptr)); |
---|
443 | |
---|
444 | if (inTinyHeap(ptr)) { |
---|
445 | lock(); |
---|
446 | tinyFree(ptr); |
---|
447 | unlock(); |
---|
448 | return; |
---|
449 | } |
---|
450 | |
---|
451 | uint32 bytes = ((uint32*)ptr)[-1]; |
---|
452 | |
---|
453 | lock(); |
---|
454 | if (bytes <= smallBufferSize) { |
---|
455 | if (smallPoolSize < maxSmallBuffers) { |
---|
456 | smallPool[smallPoolSize] = MemBlock(ptr, bytes); |
---|
457 | ++smallPoolSize; |
---|
458 | unlock(); |
---|
459 | return; |
---|
460 | } |
---|
461 | } else if (bytes <= medBufferSize) { |
---|
462 | if (medPoolSize < maxMedBuffers) { |
---|
463 | medPool[medPoolSize] = MemBlock(ptr, bytes); |
---|
464 | ++medPoolSize; |
---|
465 | unlock(); |
---|
466 | return; |
---|
467 | } |
---|
468 | } |
---|
469 | bytesAllocated -= bytes + 4; |
---|
470 | unlock(); |
---|
471 | |
---|
472 | // Free; the buffer pools are full or this is too big to store. |
---|
473 | ::free((uint8*)ptr - 4); |
---|
474 | } |
---|
475 | |
---|
476 | std::string performance() const { |
---|
477 | if (totalMallocs > 0) { |
---|
478 | int pooled = mallocsFromTinyPool + |
---|
479 | mallocsFromSmallPool + |
---|
480 | mallocsFromMedPool; |
---|
481 | |
---|
482 | int total = totalMallocs; |
---|
483 | |
---|
484 | return format("malloc performance: %5.1f%% <= %db, %5.1f%% <= %db, " |
---|
485 | "%5.1f%% <= %db, %5.1f%% > %db", |
---|
486 | 100.0 * mallocsFromTinyPool / total, |
---|
487 | BufferPool::tinyBufferSize, |
---|
488 | 100.0 * mallocsFromSmallPool / total, |
---|
489 | BufferPool::smallBufferSize, |
---|
490 | 100.0 * mallocsFromMedPool / total, |
---|
491 | BufferPool::medBufferSize, |
---|
492 | 100.0 * (1.0 - (double)pooled / total), |
---|
493 | BufferPool::medBufferSize); |
---|
494 | } else { |
---|
495 | return "No System::malloc calls made yet."; |
---|
496 | } |
---|
497 | } |
---|
498 | |
---|
499 | std::string status() const { |
---|
500 | return format("preallocated shared buffers: %5d/%d x %db", |
---|
501 | maxTinyBuffers - tinyPoolSize, maxTinyBuffers, tinyBufferSize); |
---|
502 | } |
---|
503 | }; |
---|
504 | |
---|
505 | // Dynamically allocated because we need to ensure that |
---|
506 | // the buffer pool is still around when the last global variable |
---|
507 | // is deallocated. |
---|
508 | static BufferPool* bufferpool = NULL; |
---|
509 | |
---|
510 | std::string System::mallocPerformance() { |
---|
511 | #ifndef NO_BUFFERPOOL |
---|
512 | return bufferpool->performance(); |
---|
513 | #else |
---|
514 | return "NO_BUFFERPOOL"; |
---|
515 | #endif |
---|
516 | } |
---|
517 | |
---|
518 | std::string System::mallocStatus() { |
---|
519 | #ifndef NO_BUFFERPOOL |
---|
520 | return bufferpool->status(); |
---|
521 | #else |
---|
522 | return "NO_BUFFERPOOL"; |
---|
523 | #endif |
---|
524 | } |
---|
525 | |
---|
526 | |
---|
527 | void System::resetMallocPerformanceCounters() { |
---|
528 | #ifndef NO_BUFFERPOOL |
---|
529 | bufferpool->totalMallocs = 0; |
---|
530 | bufferpool->mallocsFromMedPool = 0; |
---|
531 | bufferpool->mallocsFromSmallPool = 0; |
---|
532 | bufferpool->mallocsFromTinyPool = 0; |
---|
533 | #endif |
---|
534 | } |
---|
535 | |
---|
536 | |
---|
537 | #ifndef NO_BUFFERPOOL |
---|
538 | inline void initMem() { |
---|
539 | // Putting the test here ensures that the system is always |
---|
540 | // initialized, even when globals are being allocated. |
---|
541 | static bool initialized = false; |
---|
542 | if (! initialized) { |
---|
543 | bufferpool = new BufferPool(); |
---|
544 | initialized = true; |
---|
545 | } |
---|
546 | } |
---|
547 | #endif |
---|
548 | |
---|
549 | |
---|
550 | void* System::malloc(size_t bytes) { |
---|
551 | #ifndef NO_BUFFERPOOL |
---|
552 | initMem(); |
---|
553 | return bufferpool->malloc(bytes); |
---|
554 | #else |
---|
555 | return ::malloc(bytes); |
---|
556 | #endif |
---|
557 | } |
---|
558 | |
---|
559 | void* System::calloc(size_t n, size_t x) { |
---|
560 | #ifndef NO_BUFFERPOOL |
---|
561 | void* b = System::malloc(n * x); |
---|
562 | System::memset(b, 0, n * x); |
---|
563 | return b; |
---|
564 | #else |
---|
565 | return ::calloc(n, x); |
---|
566 | #endif |
---|
567 | } |
---|
568 | |
---|
569 | |
---|
570 | void* System::realloc(void* block, size_t bytes) { |
---|
571 | #ifndef NO_BUFFERPOOL |
---|
572 | initMem(); |
---|
573 | return bufferpool->realloc(block, bytes); |
---|
574 | #else |
---|
575 | return ::realloc(block, bytes); |
---|
576 | #endif |
---|
577 | } |
---|
578 | |
---|
579 | |
---|
580 | void System::free(void* p) { |
---|
581 | #ifndef NO_BUFFERPOOL |
---|
582 | bufferpool->free(p); |
---|
583 | #else |
---|
584 | return ::free(p); |
---|
585 | #endif |
---|
586 | } |
---|
587 | |
---|
588 | |
---|
589 | void* System::alignedMalloc(size_t bytes, size_t alignment) { |
---|
590 | alwaysAssertM(isPow2(alignment), "alignment must be a power of 2"); |
---|
591 | |
---|
592 | // We must align to at least a word boundary. |
---|
593 | alignment = iMax((int)alignment, sizeof(void *)); |
---|
594 | |
---|
595 | // Pad the allocation size with the alignment size and the |
---|
596 | // size of the redirect pointer. |
---|
597 | size_t totalBytes = bytes + alignment + sizeof(intptr_t); |
---|
598 | |
---|
599 | void* truePtr = System::malloc(totalBytes); |
---|
600 | |
---|
601 | if (!truePtr) { |
---|
602 | // malloc returned NULL |
---|
603 | return NULL; |
---|
604 | } |
---|
605 | |
---|
606 | debugAssert(isValidHeapPointer(truePtr)); |
---|
607 | #ifdef G3D_WIN32 |
---|
608 | // The blocks we return will not be valid Win32 debug heap |
---|
609 | // pointers because they are offset |
---|
610 | // debugAssert(_CrtIsValidPointer(truePtr, totalBytes, TRUE) ); |
---|
611 | #endif |
---|
612 | |
---|
613 | // The return pointer will be the next aligned location (we must at least |
---|
614 | // leave space for the redirect pointer, however). |
---|
615 | char* alignedPtr = ((char*)truePtr)+ sizeof(intptr_t); |
---|
616 | |
---|
617 | #if 0 |
---|
618 | // 2^n - 1 has the form 1111... in binary. |
---|
619 | uint32 bitMask = (alignment - 1); |
---|
620 | |
---|
621 | // Advance forward until we reach an aligned location. |
---|
622 | while ((((intptr_t)alignedPtr) & bitMask) != 0) { |
---|
623 | alignedPtr += sizeof(void*); |
---|
624 | } |
---|
625 | #else |
---|
626 | alignedPtr += alignment - (((intptr_t)alignedPtr) & (alignment - 1)); |
---|
627 | // assert((alignedPtr - truePtr) + bytes <= totalBytes); |
---|
628 | #endif |
---|
629 | |
---|
630 | debugAssert((alignedPtr - truePtr) + bytes <= totalBytes); |
---|
631 | |
---|
632 | // Immediately before the aligned location, write the true array location |
---|
633 | // so that we can free it correctly. |
---|
634 | intptr_t* redirectPtr = (intptr_t*)(alignedPtr - sizeof(intptr_t)); |
---|
635 | redirectPtr[0] = (intptr_t)truePtr; |
---|
636 | |
---|
637 | debugAssert(isValidHeapPointer(truePtr)); |
---|
638 | |
---|
639 | #ifdef G3D_WIN32 |
---|
640 | debugAssert( _CrtIsValidPointer(alignedPtr, bytes, TRUE) ); |
---|
641 | #endif |
---|
642 | return (void*)alignedPtr; |
---|
643 | } |
---|
644 | |
---|
645 | |
---|
646 | void System::alignedFree(void* _ptr) { |
---|
647 | if (_ptr == NULL) { |
---|
648 | return; |
---|
649 | } |
---|
650 | |
---|
651 | char* alignedPtr = (char*)_ptr; |
---|
652 | |
---|
653 | // Back up one word from the pointer the user passed in. |
---|
654 | // We now have a pointer to a pointer to the true start |
---|
655 | // of the memory block. |
---|
656 | intptr_t* redirectPtr = (intptr_t*)(alignedPtr - sizeof(intptr_t)); |
---|
657 | |
---|
658 | // Dereference that pointer so that ptr = true start |
---|
659 | void* truePtr = (void*)(redirectPtr[0]); |
---|
660 | |
---|
661 | debugAssert(isValidHeapPointer(truePtr)); |
---|
662 | System::free(truePtr); |
---|
663 | } |
---|
664 | |
---|
665 | |
---|
666 | } // namespace |
---|