"L. Spiro Engine"
|
00001 00032 #ifndef __LSA_STDALLOCATORHEAP_H__ 00033 #define __LSA_STDALLOCATORHEAP_H__ 00034 00035 #include "../OSHeap/LSAOsHeap.h" 00036 #if defined( LSA_USE_FLOAT_OPTIMIZATIONS ) 00037 #include <cmath> 00038 #endif // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00039 00040 00041 namespace lsa { 00042 00048 class CStdAllocatorHeap { 00049 // Only the CStdAllocator class can create this object. 00050 friend class CStdAllocator; 00051 00052 public : 00053 // == Functions. 00061 LSVOID * LSE_CALL Alloc( LSA_SIZE _sSize, LSUINT32 _ui32Align = LSA_MIN_ALIGN LSA_DEBUGPARMSDECL ); 00062 00069 LSBOOL LSE_CALL Free( LSVOID * _pvAddr ); 00070 00077 LSBOOL LSE_CALL IsEmpty( LSBOOL _bReport ) const; 00078 00082 LSVOID LSE_CALL Trash(); 00083 00093 LSA_SIZE LSE_CALL PrintAllocations( LSUINT32 _ui32Count = 0UL, LSUINT32 _ui32End = 0xFFFFFFFFUL ) const; 00094 00100 LSE_INLINE LSA_SIZE LSE_CALL GetHeapSize() const; 00101 00109 static LSUINT32 LSE_CALL GetAllocationCounter(); 00110 00111 00112 protected : 00113 // == Various constructors. 00114 LSE_INLINE LSE_CALL CStdAllocatorHeap(); 00115 00116 protected : 00117 // == Types. 00121 #pragma pack( push, 1 ) 00122 typedef struct LSA_FREEHEADER { 00126 LSA_FREEHEADER * pfhNextFree; 00127 00131 LSA_FREEHEADER * pfhPrevFree; 00132 00136 LSA_FREEHEADER * pfhNextInHash; 00137 00141 LSA_SIZE sActualSize; 00142 } * LPLSA_FREEHEADER, * const LPCLSA_FREEHEADER; 00143 #pragma pack( pop ) 00144 00149 #pragma pack( push, 1 ) 00150 typedef struct LSA_ALLOCATIONHEADER { 00154 LPLSA_FREEHEADER pfhPrevFree; 00155 00159 LSA_ALLOCATIONHEADER * pahNext; 00160 00164 LSA_ALLOCATIONHEADER * pahNextInHash; 00165 00171 LSUINT32 ui32Size : 31; 00172 00176 LSUINT32 ui32Alignment : 1; 00177 00178 // Debug information, if any. 00179 #ifdef LSA_DEBUG 00180 00183 LSUINT32 ui32Line; 00184 00188 const LSCHAR * pcFile; 00189 00193 LSUINT32 ui32Number; 00194 #endif // #ifdef LSA_DEBUG 00195 } * LPLSA_ALLOCATIONHEADER, * const LPCLSA_ALLOCATIONHEADER; 00196 #pragma pack( pop ) 00197 00198 00199 protected : 00200 // == Functions. 00207 LSE_INLINE LSVOID LSE_CALL Set( LSVOID * _pvHeap, LSA_SIZE _sSizeOfHeap ); 00208 00218 LSVOID * LSE_CALL ReAlloc( LPLSA_ALLOCATIONHEADER &_lpahHeader, LSVOID * _pvAddr, LSA_SIZE _sSize LSA_DEBUGPARMSDECL ); 00219 00220 // Given the address of a block, get the address returned to the user. 00221 LSE_INLINE static LSVOID * LSE_CALL GetStartAddress( const LPLSA_ALLOCATIONHEADER _lpahHeader ); 00222 00223 // Get the actual size of a block given the requested size, alignment, and start address. 00224 LSE_INLINE static LSA_SIZE LSE_CALL GetActualSizeOfBlock( LSVOID * _pvAddr, LSA_SIZE _sSize, LSUINT32 _ui32Align ); 00225 00226 // Get the size of an allocated block. 00227 LSE_INLINE static LSA_SIZE LSE_CALL GetSize( const LPLSA_ALLOCATIONHEADER _lpahHeader ); 00228 00229 // Given an address returned to the user, generate a hash key. 00230 LSE_INLINE LSUINT32 LSE_CALL GetHashKey( LSVOID * _pvAddr ) const; 00231 00232 // Given a size, generate a hash key. 00233 LSE_INLINE static LSUINT32 LSE_CALL GetHashKey( LSA_SIZE _sSize ); 00234 00235 // Round up to the nearest alignment. 00236 LSE_INLINE static LSA_SIZE LSE_CALL RoundUp( LSA_SIZE _sSize ); 00237 00238 // Create a size key for allocation blocks. 00239 LSE_INLINE static LSUINT32 LSE_CALL CreateSizeKey( LSA_SIZE _sSize ); 00240 00241 // Get the alignment in decimal form given an alignment key. 00242 LSE_INLINE static LSUINT32 LSE_CALL GetAlignment( LSUINT32 _ui32Key ); 00243 00244 // Get the allocation block previous to the given block, or NULL if there is none. 00245 LPLSA_ALLOCATIONHEADER LSE_CALL GetPrevAlloc( LPLSA_ALLOCATIONHEADER _pahFrom ) const; 00246 00247 // Add an allocation. Does not remove free blocks or cut anything. Assumes the address at _pvAddr 00248 // is ready to go. 00249 LSVOID * LSE_CALL AddAllocInternal( LPLSA_FREEHEADER _lpfhPrevFree, 00250 LPLSA_ALLOCATIONHEADER _lpahNextAllocated, 00251 LSVOID * _pvAddr, LSA_SIZE _sSize, LSUINT32 _ui32Align 00252 LSA_DEBUGPARMSDECL ); 00253 00254 // Add an allocation at the given address. The given address is assumed to be the header of a free 00255 // block which must be adjusted or removed to make room for the allocation block. Performs all 00256 // necessary operations for adding the allocation block. 00257 LSVOID * LSE_CALL AddAlloc( LPLSA_ALLOCATIONHEADER _lpahNextAllocated, 00258 LSVOID * _pvAddr, LSA_SIZE _sSize, LSUINT32 _ui32Align 00259 LSA_DEBUGPARMSDECL ); 00260 00261 // Remove an allocation. Does all merging and fixing, leaving in a stable state. 00262 LSVOID LSE_CALL RemAlloc( LPLSA_ALLOCATIONHEADER _lpahAllocated ); 00263 00264 // Add a free-store block without merging or fixing-up. 00265 LSVOID LSE_CALL AddFreeBlockInternal( LPLSA_FREEHEADER _lpfhAfterMe, LPLSA_FREEHEADER _lpfhBeforeMe, 00266 LSVOID * _pvAddr, LSA_SIZE _sSize ); 00267 00268 // Remove a free-store block. Does not chop, merge, or fix-up. 00269 LSVOID LSE_CALL RemFreeBlockInternal( LPLSA_FREEHEADER _lpfhRemoveMe ); 00270 00271 // Add a free-store block. Performs merging and fixing. 00272 LSVOID LSE_CALL AddFreeBlock( LPLSA_FREEHEADER _lpfhAfterMe, LPLSA_FREEHEADER _lpfhBeforeMe, 00273 LSVOID * _pvAddr, LSA_SIZE _sSize ); 00274 00275 // Merge connected free-store blocks. 00276 LSBOOL LSE_CALL MergeFreeStoreBlocks( LPLSA_FREEHEADER _lpfhFirst ); 00277 00278 // Get the head of the allocation list. 00279 LSE_INLINE LPLSA_ALLOCATIONHEADER LSE_CALL GetAllocationHead() const; 00280 00281 // Get the next allocated block after a free block or NULL if there are none. 00282 LSE_INLINE LPLSA_ALLOCATIONHEADER LSE_CALL GetNextAllocatedBlock( LPLSA_FREEHEADER _lpAfterMe ) const; 00283 00284 // Get the next free block after the given allocated block, or NULL for none. 00285 LSE_INLINE LPLSA_FREEHEADER LSE_CALL GetNextFreeBlock( LPLSA_ALLOCATIONHEADER _lpahAfterMe ); 00286 00287 // Fix up the free pointers on allocated blocks following a free block. 00288 LSE_INLINE LSVOID LSE_CALL FixUpAllocationFreeBlockPointers( LPLSA_FREEHEADER _lpfhAfterMe ); 00289 00290 // In a linked-list of allocated blocks, replace each elementÃs previous free pointer with the one given 00291 // if it matches the provided original free-block pointer. Stops scanning at the first block that does 00292 // not match the given key. 00293 LSE_INLINE LSVOID LSE_CALL ReplaceAllocationFreeBlockPointers( LPLSA_ALLOCATIONHEADER _lpahStart, LPLSA_FREEHEADER _lpfhReplaceMe, LPLSA_FREEHEADER _lpfhWithMe ); 00294 00295 // Find a free-store block large enough for a given allocation size and alignment. 00296 LPLSA_FREEHEADER LSE_CALL FindFreeStoreBlock( LSA_SIZE _sSize, LSUINT32 _ui32Align ); 00297 00298 // Find an allocated block given its address. 00299 LSE_INLINE LPLSA_ALLOCATIONHEADER LSE_CALL FindAllocationBlock( LSVOID * _pvAddr ); 00300 00301 // Additional debug functions. 00302 #ifdef LSA_DEBUG 00303 // Print free blocks. 00304 LSVOID LSE_CALL PrintFreeBlocks() const; 00305 00306 // Verify free blocks. 00307 LSVOID LSE_CALL VerifyFreeBlocks() const; 00308 00309 // Verify allocated blocks. 00310 LSVOID LSE_CALL VerifyAllocatedBlocks() const; 00311 00317 static LSVOID LSE_CALL PrintAllocation( const LPLSA_ALLOCATIONHEADER _lpahHeader ); 00318 #endif // #ifdef LSA_DEBUG 00319 00320 // == Members. 00324 LSVOID * m_pvHeap; 00325 00329 LSA_SIZE m_sSize; 00330 00334 CStdAllocatorHeap * m_psahNext; 00335 00339 LPLSA_ALLOCATIONHEADER m_lpahAllocatedBlocks[LSA_BUCKETS]; 00340 00344 LPLSA_FREEHEADER m_lpfhFreeBlocks[LSA_BUCKETS]; 00345 00349 LPLSA_FREEHEADER m_lpfhFreeHead; 00350 00354 LSUINT16 m_usLowestFreeBlockInHashTable; 00355 00356 #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00357 00360 LSDOUBLE m_dInvSize; 00361 #endif // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00362 00363 #ifdef LSA_DEBUG 00364 00367 static LSUINT32 m_ui32AllocCount; 00368 #endif // #ifdef LSA_DEBUG 00369 00370 #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00371 00374 static LSBOOL m_bMadeTable; 00375 00379 static LSDOUBLE m_dSinTable[0x10000]; 00380 #endif // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00381 }; 00382 00383 00384 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 00385 // DEFINITIONS 00386 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 00387 // == Various constructors. 00388 LSE_INLINE LSE_CALL CStdAllocatorHeap::CStdAllocatorHeap() : 00389 #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00390 m_dInvSize( 0.0 ), 00391 #endif // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00392 m_sSize( 0 ), 00393 m_pvHeap( NULL ), 00394 //m_lpahAllocatedHead( NULL ), 00395 m_lpfhFreeHead( NULL ), 00396 m_psahNext( NULL ), 00397 m_usLowestFreeBlockInHashTable( LSA_BUCKETS - 1 ) { 00398 for ( LSUINT32 I = 0; I < LSA_BUCKETS; ++I ) { 00399 m_lpahAllocatedBlocks[I] = NULL; 00400 m_lpfhFreeBlocks[I] = NULL; 00401 } 00402 #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00403 if ( !m_bMadeTable ) { 00404 for ( LSINT32 I = 0x10000; --I >= 0; ) { 00405 m_dSinTable[I] = ::sin( (2.0 * 3.1415926535897932384626433832795 / 0x10000) * I ); 00406 } 00407 m_bMadeTable = true; 00408 } 00409 #endif // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00410 } 00411 00412 // == Functions. 00418 LSE_INLINE LSA_SIZE LSE_CALL CStdAllocatorHeap::GetHeapSize() const { 00419 return m_sSize; 00420 } 00421 00428 LSE_INLINE LSVOID LSE_CALL CStdAllocatorHeap::Set( LSVOID * _pvHeap, LSA_SIZE _sSizeOfHeap ) { 00429 m_pvHeap = _pvHeap; 00430 m_sSize = _sSizeOfHeap; 00431 #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00432 m_dInvSize = 1.0 / m_sSize; 00433 #endif // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00434 //m_lpahAllocatedHead = NULL; 00435 m_lpfhFreeHead = NULL; 00436 m_psahNext = NULL; 00437 for ( LSUINT32 I = 0; I < LSA_BUCKETS; ++I ) { 00438 m_lpahAllocatedBlocks[I] = NULL; 00439 m_lpfhFreeBlocks[I] = NULL; 00440 } 00441 00442 m_usLowestFreeBlockInHashTable = LSA_BUCKETS - 1; 00443 00444 // Add the whole block to the free store. 00445 AddFreeBlock( NULL, NULL, m_pvHeap, m_sSize ); 00446 } 00447 00448 // Given the address of a block, get the address returned to the user. 00449 LSE_INLINE LSVOID * CStdAllocatorHeap::GetStartAddress( const LPLSA_ALLOCATIONHEADER _lpahHeader ) { 00450 LSA_SIZE sStart = reinterpret_cast<LSA_SIZE>(_lpahHeader); 00451 00452 // Skip the header. 00453 sStart += sizeof( LSA_ALLOCATIONHEADER ); 00454 // From here, round up based on the alignment. 00455 LSUINT32 ui32Align = LSA_MIN_ALIGN + LSA_MIN_ALIGN * _lpahHeader->ui32Alignment; 00456 return reinterpret_cast<LSVOID *>(LSE_ROUND_UP( sStart, ui32Align )); 00457 } 00458 00459 // Get the actual size of a block given the requested size, alignment, and start address. 00460 LSE_INLINE LSA_SIZE LSE_CALL CStdAllocatorHeap::GetActualSizeOfBlock( LSVOID * _pvAddr, LSA_SIZE _sSize, LSUINT32 _ui32Align ) { 00461 LSA_SIZE sStart = reinterpret_cast<LSA_SIZE>(_pvAddr); 00462 LSA_SIZE sOriginal = sStart; 00463 // Skip the header. 00464 sStart += sizeof( LSA_ALLOCATIONHEADER ); 00465 // From here, round up based on the alignment. 00466 LSUINT32 ui32Align = LSA_MIN_ALIGN + LSA_MIN_ALIGN * _ui32Align; 00467 00468 sStart = LSE_ROUND_UP( sStart, ui32Align ); 00469 00470 // Now add the requested allocation length. 00471 sStart += _sSize; 00472 00473 sStart = LSE_ROUND_UP( sStart, LSA_MIN_ALIGN ); 00474 00475 return sStart - sOriginal; 00476 } 00477 00478 // Get the size of an allocated block. 00479 LSE_INLINE LSA_SIZE CStdAllocatorHeap::GetSize( const LPLSA_ALLOCATIONHEADER _lpahHeader ) { 00480 return _lpahHeader->ui32Size * LSA_MIN_ALIGN; 00481 } 00482 00483 // Given an address returned to the user, generate a hash key. 00484 LSE_INLINE LSUINT32 CStdAllocatorHeap::GetHashKey( LSVOID * _pvAddr ) const { 00485 LSA_SUPERSIZE sStart = reinterpret_cast<LSA_SIZE>(_pvAddr); 00486 LSA_SUPERSIZE sBase = reinterpret_cast<LSA_SIZE>(m_pvHeap); 00487 sStart -= sBase; 00488 #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00489 LSDOUBLE dFrac = sStart * m_dInvSize; 00490 dFrac *= static_cast<LSDOUBLE>(1.5707963267948966192313216916398); 00491 return static_cast<LSUINT32>(m_dSinTable[static_cast<LSUINT16>(1.0/(3.1415926535897932384626433832795*2.0)*0x10000*dFrac)] * (LSA_BUCKETS - 1)); 00492 #else // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00493 sStart = sStart * LSA_BUCKETS / m_sSize; 00494 return static_cast<LSUINT32>(sStart); 00495 #endif // #ifdef LSA_USE_FLOAT_OPTIMIZATIONS 00496 } 00497 00498 // Given a size, generate a hash key. 00499 LSE_INLINE LSUINT32 CStdAllocatorHeap::GetHashKey( LSA_SIZE _sSize ) { 00500 _sSize >>= 12; 00501 if ( _sSize < LSA_BUCKETS ) { return static_cast<LSUINT32>(_sSize); } 00502 return LSA_BUCKETS - 1; 00503 } 00504 00505 // Round up to the nearest alignment. 00506 LSE_INLINE LSA_SIZE LSE_CALL CStdAllocatorHeap::RoundUp( LSA_SIZE _sSize ) { 00507 LSA_SIZE sThis = _sSize >> LSA_MIN_ALIGN_BITS; 00508 if ( (_sSize & (LSA_MIN_ALIGN - 1)) ) { 00509 return (sThis + 1) << LSA_MIN_ALIGN_BITS; 00510 } 00511 return sThis << LSA_MIN_ALIGN_BITS; 00512 } 00513 00514 // Create a size key for allocation blocks. 00515 LSE_INLINE LSUINT32 LSE_CALL CStdAllocatorHeap::CreateSizeKey( LSA_SIZE _sSize ) { 00516 LSA_SIZE sThis = _sSize >> LSA_MIN_ALIGN_BITS; 00517 if ( (_sSize & (LSA_MIN_ALIGN - 1)) ) { 00518 return static_cast<LSUINT32>(sThis + 1); 00519 } 00520 return static_cast<LSUINT32>(sThis); 00521 //return static_cast<LSUINT32>(RoundUp( _sSize ) >> LSA_MIN_ALIGN_BITS); 00522 } 00523 00524 // Get the alignment in decimal form given an alignment key. 00525 LSE_INLINE LSUINT32 LSE_CALL CStdAllocatorHeap::GetAlignment( LSUINT32 _ui32Key ) { 00526 return LSA_MIN_ALIGN + LSA_MIN_ALIGN * _ui32Key; 00527 } 00528 00529 // Get the head of the allocation list. 00530 LSE_INLINE CStdAllocatorHeap::LPLSA_ALLOCATIONHEADER LSE_CALL CStdAllocatorHeap::GetAllocationHead() const { 00531 if ( m_lpfhFreeHead == m_pvHeap ) { 00532 return GetNextAllocatedBlock( m_lpfhFreeHead ); 00533 } 00534 return static_cast<LPLSA_ALLOCATIONHEADER>(m_pvHeap); 00535 } 00536 00537 // Get the next allocated block after a free block or NULL if there are none. 00538 LSE_INLINE CStdAllocatorHeap::LPLSA_ALLOCATIONHEADER LSE_CALL CStdAllocatorHeap::GetNextAllocatedBlock( LPLSA_FREEHEADER _lpAfterMe ) const { 00539 LSA_SIZE sStart = reinterpret_cast<LSA_SIZE>(_lpAfterMe); 00540 sStart += _lpAfterMe->sActualSize; 00541 LSA_SIZE sOurEnd = reinterpret_cast<LSA_SIZE>(m_pvHeap); 00542 00543 // If it points to the end of the block or beyond there is nothing there. 00544 if ( sStart >= (sOurEnd + m_sSize) ) { 00545 return NULL; 00546 } 00547 return reinterpret_cast<LPLSA_ALLOCATIONHEADER>(sStart); 00548 } 00549 00550 // Get the next free block after the given allocated block, or NULL for none. 00551 LSE_INLINE CStdAllocatorHeap::LPLSA_FREEHEADER LSE_CALL CStdAllocatorHeap::GetNextFreeBlock( LPLSA_ALLOCATIONHEADER _lpahAfterMe ) { 00552 // Go to the free block before this allocation. 00553 LPLSA_FREEHEADER lpfhThis = _lpahAfterMe->pfhPrevFree; 00554 if ( !lpfhThis ) { 00555 return m_lpfhFreeHead; 00556 } 00557 // Return the next free block after that. 00558 return lpfhThis->pfhNextFree; 00559 } 00560 00561 // Fix up the free pointers on allocated blocks following a free block. 00562 LSE_INLINE LSVOID LSE_CALL CStdAllocatorHeap::FixUpAllocationFreeBlockPointers( LPLSA_FREEHEADER _lpfhAfterMe ) { 00563 //LSA_SIZE sIamAt = reinterpret_cast<LSA_SIZE>(_lpfhAfterMe); 00564 LSA_SIZE sEnd = reinterpret_cast<LSA_SIZE>(_lpfhAfterMe->pfhNextFree); 00565 if ( !sEnd ) { 00566 sEnd = static_cast<LSA_SIZE>(-1); 00567 } 00568 00569 /*for ( LSUINT32 I = 0; I < LSA_BUCKETS; ++I ) { 00570 for ( LPLSA_ALLOCATIONHEADER lpahThis = m_lpahAllocatedBlocks[I]; lpahThis; lpahThis = lpahThis->pahNextInHash ) { 00571 LSA_SIZE sThis = reinterpret_cast<LSA_SIZE>(lpahThis); 00572 if ( sThis > sIamAt && sThis < sEnd ) { 00573 lpahThis->pfhPrevFree = _lpfhAfterMe; 00574 } 00575 } 00576 }*/ 00577 00578 00579 LPLSA_ALLOCATIONHEADER lpahStart; 00580 for ( lpahStart = GetNextAllocatedBlock( _lpfhAfterMe ); 00581 lpahStart; 00582 lpahStart = lpahStart->pahNext ) { 00583 LSA_SIZE sItPointsTo = reinterpret_cast<LSA_SIZE>(lpahStart->pfhPrevFree); 00584 // If it points below us, we are the new closest. 00585 // Make it point at us. 00586 if ( sItPointsTo < sEnd ) { 00587 lpahStart->pfhPrevFree = _lpfhAfterMe; 00588 } 00589 else { 00590 break; 00591 } 00592 } 00593 } 00594 00595 // In a linked-list of allocated blocks, replace each elementÃs previous free pointer with the one given 00596 // if it matches the provided original free-block pointer. Stops scanning at the first block that does 00597 // not match the given key. 00598 LSE_INLINE LSVOID LSE_CALL CStdAllocatorHeap::ReplaceAllocationFreeBlockPointers( LPLSA_ALLOCATIONHEADER _lpahStart, LPLSA_FREEHEADER _lpfhReplaceMe, LPLSA_FREEHEADER _lpfhWithMe ) { 00599 for ( ; _lpahStart; _lpahStart = _lpahStart->pahNext ) { 00600 if ( _lpahStart->pfhPrevFree != _lpfhReplaceMe ) { return; } 00601 _lpahStart->pfhPrevFree = _lpfhWithMe; 00602 } 00603 } 00604 00605 // Find an allocated block given its address. 00606 LSE_INLINE CStdAllocatorHeap::LPLSA_ALLOCATIONHEADER LSE_CALL CStdAllocatorHeap::FindAllocationBlock( LSVOID * _pvAddr ) { 00607 LSUINT32 ui32Key = GetHashKey( _pvAddr ); 00608 // For every allocation in the hash table. 00609 for ( LPLSA_ALLOCATIONHEADER lpahThis = m_lpahAllocatedBlocks[ui32Key]; lpahThis; lpahThis = lpahThis->pahNextInHash ) { 00610 if ( GetStartAddress( lpahThis ) == _pvAddr ) { 00611 return lpahThis; 00612 } 00613 } 00614 return NULL; 00615 } 00616 00617 00618 } // namespace lsa 00619 00620 #endif // __LSA_STDALLOCATORHEAP_H__