📄 smallobj.cpp
字号:
return true; } if ( IsFilled() ) // Useless to do further corruption checks if all blocks allocated. return false; unsigned char index = firstAvailableBlock_; if ( numBlocks <= index ) { // Contents at this Chunk corrupted. This might mean something has // overwritten memory owned by the Chunks container. assert( false ); return true; } if ( !checkIndexes ) // Caller chose to skip more complex corruption tests. return false; /* If the bit at index was set in foundBlocks, then the stealth index was found on the linked-list. */ std::bitset< UCHAR_MAX > foundBlocks; unsigned char * nextBlock = NULL; /* The loop goes along singly linked-list of stealth indexes and makes sure that each index is within bounds (0 <= index < numBlocks) and that the index was not already found while traversing the linked-list. The linked- list should have exactly blocksAvailable_ nodes, so the for loop will not check more than blocksAvailable_. This loop can't check inside allocated blocks for corruption since such blocks are not within the linked-list. Contents of allocated blocks are not changed by Chunk. Here are the types of corrupted link-lists which can be verified. The corrupt index is shown with asterisks in each example. Type 1: Index is too big. numBlocks == 64 blocksAvailable_ == 7 firstAvailableBlock_ -> 17 -> 29 -> *101* There should be no indexes which are equal to or larger than the total number of blocks. Such an index would refer to a block beyond the Chunk's allocated domain. Type 2: Index is repeated. numBlocks == 64 blocksAvailable_ == 5 firstAvailableBlock_ -> 17 -> 29 -> 53 -> *17* -> 29 -> 53 ... No index should be repeated within the linked-list since that would indicate the presence of a loop in the linked-list. */ for ( unsigned char cc = 0; ; ) { nextBlock = pData_ + ( index * blockSize ); foundBlocks.set( index, true ); ++cc; if ( cc >= blocksAvailable_ ) // Successfully counted off number of nodes in linked-list. break; index = *nextBlock; if ( numBlocks <= index ) { /* This catches Type 1 corruptions as shown in above comments. This implies that a block was corrupted due to a stray pointer or an operation on a nearby block overran the size of the block. */ assert( false ); return true; } if ( foundBlocks.test( index ) ) { /* This catches Type 2 corruptions as shown in above comments. This implies that a block was corrupted due to a stray pointer or an operation on a nearby block overran the size of the block. Or perhaps the program tried to delete a block more than once. */ assert( false ); return true; } } if ( foundBlocks.count() != blocksAvailable_ ) { /* This implies that the singly-linked-list of stealth indexes was corrupted. Ideally, this should have been detected within the loop. */ assert( false ); return true; } return false;}// Chunk::IsBlockAvailable ----------------------------------------------------bool Chunk::IsBlockAvailable( void * p, unsigned char numBlocks, std::size_t blockSize ) const{ (void) numBlocks; if ( IsFilled() ) return false; unsigned char * place = static_cast< unsigned char * >( p ); // Alignment check assert( ( place - pData_ ) % blockSize == 0 ); unsigned char blockIndex = static_cast< unsigned char >( ( place - pData_ ) / blockSize ); unsigned char index = firstAvailableBlock_; assert( numBlocks > index ); if ( index == blockIndex ) return true; /* If the bit at index was set in foundBlocks, then the stealth index was found on the linked-list. */ std::bitset< UCHAR_MAX > foundBlocks; unsigned char * nextBlock = NULL; for ( unsigned char cc = 0; ; ) { nextBlock = pData_ + ( index * blockSize ); foundBlocks.set( index, true ); ++cc; if ( cc >= blocksAvailable_ ) // Successfully counted off number of nodes in linked-list. break; index = *nextBlock; if ( index == blockIndex ) return true; assert( numBlocks > index ); assert( !foundBlocks.test( index ) ); } return false;}// FixedAllocator::FixedAllocator ---------------------------------------------FixedAllocator::FixedAllocator() : blockSize_( 0 ) , numBlocks_( 0 ) , chunks_( 0 ) , allocChunk_( NULL ) , deallocChunk_( NULL ) , emptyChunk_( NULL ){}// FixedAllocator::~FixedAllocator --------------------------------------------FixedAllocator::~FixedAllocator(){#ifdef DO_EXTRA_LOKI_TESTS TrimEmptyChunk(); assert( chunks_.empty() && "Memory leak detected!" );#endif for ( ChunkIter i( chunks_.begin() ); i != chunks_.end(); ++i ) i->Release();}// FixedAllocator::Initialize -------------------------------------------------void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize ){ assert( blockSize > 0 ); assert( pageSize >= blockSize ); blockSize_ = blockSize; std::size_t numBlocks = pageSize / blockSize; if ( numBlocks > MaxObjectsPerChunk_ ) numBlocks = MaxObjectsPerChunk_; else if ( numBlocks < MinObjectsPerChunk_ ) numBlocks = MinObjectsPerChunk_; numBlocks_ = static_cast<unsigned char>(numBlocks); assert(numBlocks_ == numBlocks);}// FixedAllocator::CountEmptyChunks -------------------------------------------std::size_t FixedAllocator::CountEmptyChunks( void ) const{#ifdef DO_EXTRA_LOKI_TESTS // This code is only used for specialized tests of the allocator. // It is #ifdef-ed so that its O(C) complexity does not overwhelm the // functions which call it. std::size_t count = 0; for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it ) { const Chunk & chunk = *it; if ( chunk.HasAvailable( numBlocks_ ) ) ++count; } return count;#else return ( NULL == emptyChunk_ ) ? 0 : 1;#endif}// FixedAllocator::IsCorrupt --------------------------------------------------bool FixedAllocator::IsCorrupt( void ) const{ const bool isEmpty = chunks_.empty(); ChunkCIter start( chunks_.begin() ); ChunkCIter last( chunks_.end() ); const size_t emptyChunkCount = CountEmptyChunks(); if ( isEmpty ) { if ( start != last ) { assert( false ); return true; } if ( 0 < emptyChunkCount ) { assert( false ); return true; } if ( NULL != deallocChunk_ ) { assert( false ); return true; } if ( NULL != allocChunk_ ) { assert( false ); return true; } if ( NULL != emptyChunk_ ) { assert( false ); return true; } } else { const Chunk * front = &chunks_.front(); const Chunk * back = &chunks_.back(); if ( start >= last ) { assert( false ); return true; } if ( back < deallocChunk_ ) { assert( false ); return true; } if ( back < allocChunk_ ) { assert( false ); return true; } if ( front > deallocChunk_ ) { assert( false ); return true; } if ( front > allocChunk_ ) { assert( false ); return true; } switch ( emptyChunkCount ) { case 0: if ( emptyChunk_ != NULL ) { assert( false ); return true; } break; case 1: if ( emptyChunk_ == NULL ) { assert( false ); return true; } if ( back < emptyChunk_ ) { assert( false ); return true; } if ( front > emptyChunk_ ) { assert( false ); return true; } if ( !emptyChunk_->HasAvailable( numBlocks_ ) ) { // This may imply somebody tried to delete a block twice. assert( false ); return true; } break; default: assert( false ); return true; } for ( ChunkCIter it( start ); it != last; ++it ) { const Chunk & chunk = *it; if ( chunk.IsCorrupt( numBlocks_, blockSize_, true ) ) return true; } } return false;}// FixedAllocator::HasBlock ---------------------------------------------------const Chunk * FixedAllocator::HasBlock( void * p ) const{ const std::size_t chunkLength = numBlocks_ * blockSize_; for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it ) { const Chunk & chunk = *it; if ( chunk.HasBlock( p, chunkLength ) ) return &chunk; } return NULL;}// FixedAllocator::TrimEmptyChunk ---------------------------------------------bool FixedAllocator::TrimEmptyChunk( void ){ // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk. assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) ); if ( NULL == emptyChunk_ ) return false; // If emptyChunk_ points to valid Chunk, then chunk list is not empty. assert( !chunks_.empty() ); // And there should be exactly 1 empty Chunk. assert( 1 == CountEmptyChunks() ); Chunk * lastChunk = &chunks_.back(); if ( lastChunk != emptyChunk_ ) std::swap( *emptyChunk_, *lastChunk ); assert( lastChunk->HasAvailable( numBlocks_ ) ); lastChunk->Release(); chunks_.pop_back(); if ( chunks_.empty() ) { allocChunk_ = NULL; deallocChunk_ = NULL; } else { if ( deallocChunk_ == emptyChunk_ ) { deallocChunk_ = &chunks_.front(); assert( deallocChunk_->blocksAvailable_ < numBlocks_ ); } if ( allocChunk_ == emptyChunk_ ) { allocChunk_ = &chunks_.back(); assert( allocChunk_->blocksAvailable_ < numBlocks_ ); } } emptyChunk_ = NULL; assert( 0 == CountEmptyChunks() ); return true;}// FixedAllocator::TrimChunkList ----------------------------------------------bool FixedAllocator::TrimChunkList( void ){ if ( chunks_.empty() ) { assert( NULL == allocChunk_ ); assert( NULL == deallocChunk_ ); } if ( chunks_.size() == chunks_.capacity() ) return false; // Use the "make-a-temp-and-swap" trick to remove excess capacity. Chunks( chunks_ ).swap( chunks_ ); return true;}// FixedAllocator::MakeNewChunk -----------------------------------------------bool FixedAllocator::MakeNewChunk( void ){ bool allocated = false; try { std::size_t size = chunks_.size(); // Calling chunks_.reserve *before* creating and initializing the new // Chunk means that nothing is leaked by this function in case an // exception is thrown from reserve. if ( chunks_.capacity() == size ) { if ( 0 == size ) size = 4; chunks_.reserve( size * 2 ); } Chunk newChunk; allocated = newChunk.Init( blockSize_, numBlocks_ ); if ( allocated ) chunks_.push_back( newChunk ); } catch ( ... )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -