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