bool FixedAllocator::Deallocate( void * p, Chunk * hint ) { assert(!chunks_.empty()); assert(&chunks_.front() <= deallocChunk_); assert(&chunks_.back() >= deallocChunk_); assert( &chunks_.front() <= allocChunk_ ); assert( &chunks_.back() >= allocChunk_ ); assert( CountEmptyChunks() < 2 ); Chunk * foundChunk = ( NULL == hint ) ? VicinityFind( p ) : hint; if ( NULL == foundChunk ) return false; assert( foundChunk->HasBlock( p, numBlocks_ * blockSize_ ) ); #ifdef LOKI_CHECK_FOR_CORRUPTION if ( foundChunk->IsCorrupt( numBlocks_, blockSize_, true ) ) { assert( false ); return false; } if ( foundChunk->IsBlockAvailable( p, numBlocks_, blockSize_ ) ) { assert( false ); return false; } #endif deallocChunk_ = foundChunk; DoDeallocate(p); assert( CountEmptyChunks() < 2 ); return true; }
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; }
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; }
char* allocate(size_t size) { assert(size <= chunk_size); if (chunks.empty() || (next_free + size) > chunk_size) { char* chunk = new char[chunk_size]; chunks.push_back(chunk); next_free = 0; } char* ptr = chunks.back() + next_free; next_free += size; return ptr; }
void FixedAllocator::Deallocate(void *p) { assert(last_dealloc_); assert(!chunks_.empty()); // find the chunk that holds the pointer. last_dealloc_ = FindChunk(p); assert(last_dealloc_); // found the chunk, so forwards deallocation request to it. last_dealloc_->Deallocate(p, block_size_); // the chunk is empty, should we free it? if (last_dealloc_->blocks_available_ == blocks_) { MemChunk *last_chunk = &chunks_.back(); if (last_chunk = last_dealloc_) { if (chunks_.size() > 1 && last_dealloc_[-1].blocks_available_ == blocks_) { // there are two free chunk, so free one. last_chunk->Free(); chunks_.pop_back(); last_alloc_ = last_dealloc_ = &chunks_.front(); } } else if (last_chunk->blocks_available_ == blocks_) { // there are two free chunk, so free one. last_chunk->Free(); chunks_.pop_back(); last_alloc_ = last_dealloc_; } else { // move the empty chunk to the bottom. std::swap(*last_dealloc_, *last_chunk); last_alloc_ = last_dealloc_; } } }
FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p ) { if ( chunks_.empty() ) return NULL; assert(deallocChunk_); unsigned char * pc = static_cast< unsigned char * >( p ); const std::size_t chunkLength = numBlocks_ * blockSize_; Chunk* lo = deallocChunk_; Chunk* hi = deallocChunk_ + 1; Chunk* loBound = &chunks_.front(); Chunk* hiBound = &chunks_.back() + 1; // Special case: deallocChunk_ is the last in the array if (hi == hiBound) hi = NULL; for (;;) { if (lo) { if ( lo->HasBlock( pc, chunkLength ) ) return lo; if ( lo == loBound ) { lo = NULL; if ( NULL == hi ) break; } else --lo; } if (hi) { if ( hi->HasBlock( pc, chunkLength ) ) return hi; if ( ++hi == hiBound ) { hi = NULL; if ( NULL == lo ) break; } } } return NULL; }
bool FixedAllocator::Deallocate( void * p, bool doChecks ) { if ( doChecks ) { assert(!chunks_.empty()); assert(&chunks_.front() <= deallocChunk_); assert(&chunks_.back() >= deallocChunk_); assert( &chunks_.front() <= allocChunk_ ); assert( &chunks_.back() >= allocChunk_ ); } Chunk * foundChunk = VicinityFind( p ); if ( doChecks ) { assert( NULL != foundChunk ); } else if ( NULL == foundChunk ) return false; deallocChunk_ = foundChunk; DoDeallocate(p); return true; }
MemChunk * FixedAllocator::FindChunk(void *p) { assert(last_dealloc_); assert(!chunks_.empty()); MemChunk *lo_b = &chunks_.front(); MemChunk *hi_b = &chunks_.back()+1; MemChunk *lo = last_dealloc_; MemChunk *hi = last_dealloc_+1; if (hi == hi_b) hi = NULL; for (;;) { if (lo) { if (InChunk(lo, p)) return lo; if (lo == lo_b) lo = NULL; else --lo; } if (hi) { if (InChunk(hi, p)) return hi; if (++hi == hi_b) hi = NULL; } } // Never gets here. assert(false); return NULL; }
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; }