void *Heap::CAlloc(size_t aCount, size_t aSize) { void * ret = NULL; Block * prev = NULL; Block * curr = NULL; uint16_t size = static_cast<uint16_t>(aCount * aSize); VerifyOrExit(size); size += kAlignSize - 1 - kBlockRemainderSize; size &= ~(kAlignSize - 1); size += kBlockRemainderSize; prev = &BlockSuper(); curr = &BlockNext(*prev); while (curr->GetSize() < size) { prev = curr; curr = &BlockNext(*curr); } VerifyOrExit(curr->IsFree()); prev->SetNext(curr->GetNext()); if (curr->GetSize() > size + sizeof(Block)) { const uint16_t newBlockSize = curr->GetSize() - size - sizeof(Block); curr->SetSize(size); Block &newBlock = BlockRight(*curr); newBlock.SetSize(newBlockSize); newBlock.SetNext(0); if (prev->GetSize() < newBlockSize) { BlockInsert(*prev, newBlock); } else { BlockInsert(BlockSuper(), newBlock); } } curr->SetNext(0); memset(curr->GetPointer(), 0, size); ret = curr->GetPointer(); exit: return ret; }
void Heap::BlockInsert(Block &aPrev, Block &aBlock) { Block *prev = &aPrev; for (Block *block = &BlockNext(*prev); block->GetSize() < aBlock.GetSize(); block = &BlockNext(*block)) { prev = block; } aBlock.SetNext(prev->GetNext()); prev->SetNext(BlockOffset(aBlock)); }
Block::Block(const Block& block) { // Declare necessary variables Unit* pUnit; Unit* pNewUnit; // Initialize pimpl object _pPimpl = new BlockPimpl; assert(_pPimpl); // Get Block Data this->SetPosition (block.GetPosition ()); this->SetSize (block.GetSize ()); this->SetStartPoint (block.GetStartPoint ()); // Get Unit Data pUnit = block.FirstUnit(); if (pUnit == NULL) { return; } do { pNewUnit = new Unit(*pUnit); if (pNewUnit == NULL) { continue; } this->InsertUnit(pNewUnit); } while ((pUnit = block.NextUnit()) != NULL); }
// AllocateBlock Block * BlockAllocator::Area::AllocateBlock(size_t usableSize, bool dontDefragment) { if (kMinBlockSize != block_align_ceil(sizeof(TFreeBlock))) { FATAL(("kMinBlockSize is not correctly initialized! Is %lu, but should be: " "%lu\n", kMinBlockSize, block_align_ceil(sizeof(TFreeBlock)))); BA_PANIC("kMinBlockSize not correctly initialized."); return NULL; } if (usableSize == 0) return NULL; Block *newBlock = NULL; size_t size = max(usableSize + sizeof(BlockHeader), kMinBlockSize); size = block_align_ceil(size); if (size <= _GetBlockFreeBytes()) { // find first fit TFreeBlock *block = _FindFreeBlock(size); if (!block && !dontDefragment) { // defragmenting is necessary _Defragment(); block = _FindFreeBlock(size); if (!block) { // no free block // Our data structures seem to be corrupted, since // _GetBlockFreeBytes() promised that we would have enough // free space. FATAL(("Couldn't find free block of min size %lu after " "defragmenting, although we should have %lu usable free " "bytes!\n", size, _GetBlockFreeBytes())); BA_PANIC("Bad area free bytes."); } } if (block) { // found a free block size_t remainder = block->GetSize() - size; if (remainder >= kMinBlockSize) { // enough space left for a free block Block *freePrev = block->GetPreviousBlock(); // TFreeBlock *prevFree = block->GetPreviousFreeBlock(); // TFreeBlock *nextFree = block->GetNextFreeBlock(); // newBlock = block; _MoveResizeFreeBlock(block, size, remainder); // setup the new block // newBlock->SetSize(size, true); // newBlock->SetFree(false); newBlock = _MakeUsedBlock(block, 0, freePrev, size, true); } else { // not enough space left: take the free block over completely // remove the block from the free list _RemoveFreeBlock(block); newBlock = block; newBlock->SetFree(false); } if (fFreeBlockCount) fFreeBytes -= newBlock->GetSize(); else fFreeBytes = 0; fUsedBlockCount++; } } D(SanityCheck()); return newBlock; }
// 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; }
void Heap::Free(void *aPointer) { if (aPointer == NULL) { return; } Block &block = BlockOf(aPointer); Block &right = BlockRight(block); if (IsLeftFree(block)) { Block *prev = &BlockSuper(); Block *left = &BlockNext(*prev); for (const uint16_t offset = block.GetLeftNext(); left->GetNext() != offset; left = &BlockNext(*left)) { prev = left; } // Remove left from free list. prev->SetNext(left->GetNext()); left->SetNext(0); if (right.IsFree()) { if (right.GetSize() > left->GetSize()) { for (const uint16_t offset = BlockOffset(right); prev->GetNext() != offset; prev = &BlockNext(*prev)) ; } else { prev = &BlockPrev(right); } // Remove right from free list. prev->SetNext(right.GetNext()); right.SetNext(0); // Add size of right. left->SetSize(left->GetSize() + right.GetSize() + sizeof(Block)); } // Add size of current block. left->SetSize(left->GetSize() + block.GetSize() + sizeof(Block)); BlockInsert(*prev, *left); } else { if (right.IsFree()) { Block &prev = BlockPrev(right); prev.SetNext(right.GetNext()); block.SetSize(block.GetSize() + right.GetSize() + sizeof(Block)); BlockInsert(prev, block); } else { BlockInsert(BlockSuper(), block); } } }