/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrMemoryPool.h" #if GR_DEBUG #define VALIDATE this->validate() #else #define VALIDATE #endif GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) { GR_DEBUGCODE(fAllocationCnt = 0); minAllocSize = GrMax(minAllocSize, 1 << 10); fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment), fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment); fPreallocSize = GrMax(fPreallocSize, fMinAllocSize); fHead = CreateBlock(fPreallocSize); fTail = fHead; fHead->fNext = NULL; fHead->fPrev = NULL; VALIDATE; }; GrMemoryPool::~GrMemoryPool() { VALIDATE; GrAssert(0 == fAllocationCnt); GrAssert(fHead == fTail); GrAssert(0 == fHead->fLiveCount); DeleteBlock(fHead); }; void* GrMemoryPool::allocate(size_t size) { VALIDATE; size = GrSizeAlignUp(size, kAlignment); size += kPerAllocPad; if (fTail->fFreeSize < size) { int blockSize = size; blockSize = GrMax(blockSize, fMinAllocSize); BlockHeader* block = CreateBlock(blockSize); block->fPrev = fTail; block->fNext = NULL; GrAssert(NULL == fTail->fNext); fTail->fNext = block; fTail = block; } GrAssert(fTail->fFreeSize >= size); intptr_t ptr = fTail->fCurrPtr; // We stash a pointer to the block header, just before the allocated space, // so that we can decrement the live count on delete in constant time. *reinterpret_cast(ptr) = fTail; ptr += kPerAllocPad; fTail->fPrevPtr = fTail->fCurrPtr; fTail->fCurrPtr += size; fTail->fFreeSize -= size; fTail->fLiveCount += 1; GR_DEBUGCODE(++fAllocationCnt); VALIDATE; return reinterpret_cast(ptr); } void GrMemoryPool::release(void* p) { VALIDATE; intptr_t ptr = reinterpret_cast(p) - kPerAllocPad; BlockHeader* block = *reinterpret_cast(ptr); if (1 == block->fLiveCount) { // the head block is special, it is reset rather than deleted if (fHead == block) { fHead->fCurrPtr = reinterpret_cast(fHead) + kHeaderSize; fHead->fLiveCount = 0; fHead->fFreeSize = fPreallocSize; } else { BlockHeader* prev = block->fPrev; BlockHeader* next = block->fNext; GrAssert(prev); prev->fNext = next; if (next) { next->fPrev = prev; } else { GrAssert(fTail == block); fTail = prev; } DeleteBlock(block); } } else { --block->fLiveCount; // Trivial reclaim: if we're releasing the most recent allocation, reuse it if (block->fPrevPtr == ptr) { block->fFreeSize += (block->fCurrPtr - block->fPrevPtr); block->fCurrPtr = block->fPrevPtr; } } GR_DEBUGCODE(--fAllocationCnt); VALIDATE; } GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) { BlockHeader* block = reinterpret_cast(GrMalloc(size + kHeaderSize)); // we assume malloc gives us aligned memory GrAssert(!(reinterpret_cast(block) % kAlignment)); block->fLiveCount = 0; block->fFreeSize = size; block->fCurrPtr = reinterpret_cast(block) + kHeaderSize; block->fPrevPtr = 0; // gcc warns on assigning NULL to an intptr_t. return block; } void GrMemoryPool::DeleteBlock(BlockHeader* block) { GrFree(block); } void GrMemoryPool::validate() { BlockHeader* block = fHead; BlockHeader* prev = NULL; GrAssert(block); int allocCount = 0; do { allocCount += block->fLiveCount; GrAssert(prev == block->fPrev); if (NULL != prev) { GrAssert(prev->fNext == block); } intptr_t b = reinterpret_cast(block); size_t ptrOffset = block->fCurrPtr - b; size_t totalSize = ptrOffset + block->fFreeSize; size_t userSize = totalSize - kHeaderSize; intptr_t userStart = b + kHeaderSize; GrAssert(!(b % kAlignment)); GrAssert(!(totalSize % kAlignment)); GrAssert(!(userSize % kAlignment)); GrAssert(!(block->fCurrPtr % kAlignment)); if (fHead != block) { GrAssert(block->fLiveCount); GrAssert(userSize >= fMinAllocSize); } else { GrAssert(userSize == fPreallocSize); } if (!block->fLiveCount) { GrAssert(ptrOffset == kHeaderSize); GrAssert(userStart == block->fCurrPtr); } else { GrAssert(block == *reinterpret_cast(userStart)); } prev = block; } while ((block = block->fNext)); GrAssert(allocCount == fAllocationCnt); GrAssert(prev == fTail); }