// SanityCheck bool BlockAllocator::Area::SanityCheck() const { // area ID if (fID < 0) { FATAL(("Area ID < 0: %lx\n", fID)); BA_PANIC("Bad area ID."); return false; } // size size_t areaHeaderSize = block_align_ceil(sizeof(Area)); if (fSize < areaHeaderSize + sizeof(TFreeBlock)) { FATAL(("Area too small to contain area header and at least one free " "block: %lu bytes\n", fSize)); BA_PANIC("Bad area size."); return false; } // free bytes if (fFreeBytes > fSize) { FATAL(("Free size greater than area size: %lu vs %lu\n", fFreeBytes, fSize)); BA_PANIC("Bad area free bytes."); return false; } // block count if (fFreeBlockCount + fUsedBlockCount == 0) { FATAL(("Area contains no blocks at all.\n")); BA_PANIC("Bad area block count."); return false; } // block list uint32 usedBlockCount = 0; uint32 freeBlockCount = 0; size_t freeBytes = 0; if (!fFirstBlock || !fLastBlock) { FATAL(("Invalid block list: first or last block NULL: first: %p, " "last: %p\n", fFirstBlock, fLastBlock)); BA_PANIC("Bad area block list."); return false; } else { // iterate through block list and also check free list int32 blockCount = fFreeBlockCount + fUsedBlockCount; Block *block = fFirstBlock; Block *prevBlock = NULL; Block *prevFree = NULL; Block *nextFree = fFirstFree; bool blockListOK = true; for (int32 i = 0; i < blockCount; i++) { blockListOK = false; if (!block) { FATAL(("Encountered NULL in block list at index %ld, although " "list should have %ld blocks\n", i, blockCount)); BA_PANIC("Bad area block list."); return false; } uint64 address = (uint32)block; // block within area? if (address < (uint32)this + areaHeaderSize || address + sizeof(TFreeBlock) > (uint32)this + fSize) { FATAL(("Utterly mislocated block: %p, area: %p, " "size: %lu\n", block, this, fSize)); BA_PANIC("Bad area block."); return false; } // block too large for area? size_t blockSize = block->GetSize(); if (blockSize < sizeof(TFreeBlock) || address + blockSize > (uint32)this + fSize) { FATAL(("Mislocated block: %p, size: %lu, area: %p, " "size: %lu\n", block, blockSize, this, fSize)); BA_PANIC("Bad area block."); return false; } // alignment if (block_align_floor(address) != address || block_align_floor(blockSize) != blockSize) { FATAL(("Block %ld not properly aligned: %p, size: %lu\n", i, block, blockSize)); BA_PANIC("Bad area block."); return false; } // previous block if (block->GetPreviousBlock() != prevBlock) { FATAL(("Previous block of block %ld was not the previous " "block in list: %p vs %p\n", i, block->GetPreviousBlock(), prevBlock)); BA_PANIC("Bad area block list."); return false; } // additional checks for free block list if (block->IsFree()) { freeBlockCount++; TFreeBlock *freeBlock = block->ToFreeBlock(); if (prevFree) freeBytes += freeBlock->GetSize(); else freeBytes += freeBlock->GetUsableSize(); // block == next free block of previous free block if (freeBlock != nextFree) { FATAL(("Free block %ld is not the next block in free " "list: %p vs %p\n", i, freeBlock, nextFree)); BA_PANIC("Bad area free list."); return false; } // previous free block if (freeBlock->GetPreviousFreeBlock() != prevFree) { FATAL(("Previous free block of block %ld was not the " " previous block in free list: %p vs %p\n", i, freeBlock->GetPreviousFreeBlock(), prevFree)); BA_PANIC("Bad area free list."); return false; } prevFree = freeBlock; nextFree = freeBlock->GetNextFreeBlock(); } else usedBlockCount++; prevBlock = block; block = block->GetNextBlock(); blockListOK = true; } // final checks on block list if (blockListOK) { if (block) { FATAL(("More blocks in block list than expected\n")); BA_PANIC("Bad area block count."); return false; } else if (fLastBlock != prevBlock) { FATAL(("last block in block list was %p, but should be " "%p\n", fLastBlock, prevBlock)); BA_PANIC("Bad area last block."); return false; } else if (prevFree != fLastFree) { FATAL(("last block in free list was %p, but should be %p\n", fLastFree, prevFree)); BA_PANIC("Bad area last free block."); return false; } // block counts (a bit reduntant) if (freeBlockCount != fFreeBlockCount) { FATAL(("Free block count is %ld, but should be %ld\n", fFreeBlockCount, freeBlockCount)); BA_PANIC("Bad area free block count."); return false; } if (usedBlockCount != fUsedBlockCount) { FATAL(("Used block count is %ld, but should be %ld\n", fUsedBlockCount, usedBlockCount)); BA_PANIC("Bad area used block count."); return false; } // free bytes if (fFreeBytes != freeBytes) { FATAL(("Free bytes is %lu, but should be %lu\n", fFreeBytes, freeBytes)); BA_PANIC("Bad area free bytes."); return false; } } } return true; }
// ResizeBlock Block * BlockAllocator::Area::ResizeBlock(Block *block, size_t newUsableSize, bool dontDefragment) { //PRINT(("Area::ResizeBlock(%p, %lu)\n", block, newUsableSize)); // newUsableSize must be >0 ! if (newUsableSize == 0) return NULL; Block *resultBlock = NULL; if (block) { size_t size = block->GetSize(); size_t newSize = max(newUsableSize + sizeof(BlockHeader), kMinBlockSize); newSize = block_align_ceil(newSize); if (newSize == size) { // size doesn't change: nothing to do resultBlock = block; } else if (newSize < size) { // shrink the block size_t sizeDiff = size - newSize; Block *nextBlock = block->GetNextBlock(); if (nextBlock && nextBlock->IsFree()) { // join the space with the adjoining free block TFreeBlock *freeBlock = nextBlock->ToFreeBlock(); _MoveResizeFreeBlock(freeBlock, -sizeDiff, freeBlock->GetSize() + sizeDiff); // resize the block and we're done block->SetSize(newSize, true); fFreeBytes += sizeDiff; } else if (sizeDiff >= sizeof(TFreeBlock)) { // the freed space is large enough for a free block TFreeBlock *newFree = _MakeFreeBlock(block, newSize, block, sizeDiff, nextBlock, NULL, NULL); _InsertFreeBlock(newFree); block->SetSize(newSize, true); if (fFreeBlockCount == 1) fFreeBytes += newFree->GetUsableSize(); else fFreeBytes += newFree->GetSize(); if (!dontDefragment && _DefragmentingRecommended()) _Defragment(); } // else: insufficient space for a free block: no changes resultBlock = block; } else { //PRINT((" grow...\n")); // grow the block size_t sizeDiff = newSize - size; Block *nextBlock = block->GetNextBlock(); if (nextBlock && nextBlock->IsFree() && nextBlock->GetSize() >= sizeDiff) { //PRINT((" adjoining free block\n")); // there is a adjoining free block and it is large enough TFreeBlock *freeBlock = nextBlock->ToFreeBlock(); size_t freeSize = freeBlock->GetSize(); if (freeSize - sizeDiff >= sizeof(TFreeBlock)) { // the remaining space is still large enough for a free // block _MoveResizeFreeBlock(freeBlock, sizeDiff, freeSize - sizeDiff); block->SetSize(newSize, true); fFreeBytes -= sizeDiff; } else { // the remaining free space wouldn't be large enough for // a free block: consume the free block completely Block *freeNext = freeBlock->GetNextBlock(); _RemoveFreeBlock(freeBlock); block->SetSize(size + freeSize, freeNext); _FixBlockList(block, block->GetPreviousBlock(), freeNext); if (fFreeBlockCount == 0) fFreeBytes = 0; else fFreeBytes -= freeSize; } resultBlock = block; } else { //PRINT((" no adjoining free block\n")); // no (large enough) adjoining free block: allocate // a new block and copy the data to it BlockReference *reference = block->GetReference(); resultBlock = AllocateBlock(newUsableSize, dontDefragment); block = reference->GetBlock(); if (resultBlock) { resultBlock->SetReference(reference); memcpy(resultBlock->GetData(), block->GetData(), block->GetUsableSize()); FreeBlock(block, dontDefragment); resultBlock = reference->GetBlock(); } } } } D(SanityCheck()); //PRINT(("Area::ResizeBlock() done: %p\n", resultBlock)); return resultBlock; }