| /* | 
 |  * 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" | 
 |  | 
 | #ifdef SK_DEBUG | 
 |     #define VALIDATE this->validate() | 
 | #else | 
 |     #define VALIDATE | 
 | #endif | 
 |  | 
 | GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) { | 
 |     SkDEBUGCODE(fAllocationCnt = 0); | 
 |  | 
 |     minAllocSize = GrMax<size_t>(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; | 
 |     SkASSERT(0 == fAllocationCnt); | 
 |     SkASSERT(fHead == fTail); | 
 |     SkASSERT(0 == fHead->fLiveCount); | 
 |     DeleteBlock(fHead); | 
 | }; | 
 |  | 
 | void* GrMemoryPool::allocate(size_t size) { | 
 |     VALIDATE; | 
 |     size = GrSizeAlignUp(size, kAlignment); | 
 |     size += kPerAllocPad; | 
 |     if (fTail->fFreeSize < size) { | 
 |         size_t blockSize = size; | 
 |         blockSize = GrMax<size_t>(blockSize, fMinAllocSize); | 
 |         BlockHeader* block = CreateBlock(blockSize); | 
 |  | 
 |         block->fPrev = fTail; | 
 |         block->fNext = NULL; | 
 |         SkASSERT(NULL == fTail->fNext); | 
 |         fTail->fNext = block; | 
 |         fTail = block; | 
 |     } | 
 |     SkASSERT(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<BlockHeader**>(ptr) = fTail; | 
 |     ptr += kPerAllocPad; | 
 |     fTail->fPrevPtr = fTail->fCurrPtr; | 
 |     fTail->fCurrPtr += size; | 
 |     fTail->fFreeSize -= size; | 
 |     fTail->fLiveCount += 1; | 
 |     SkDEBUGCODE(++fAllocationCnt); | 
 |     VALIDATE; | 
 |     return reinterpret_cast<void*>(ptr); | 
 | } | 
 |  | 
 | void GrMemoryPool::release(void* p) { | 
 |     VALIDATE; | 
 |     intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad; | 
 |     BlockHeader* block = *reinterpret_cast<BlockHeader**>(ptr); | 
 |     if (1 == block->fLiveCount) { | 
 |         // the head block is special, it is reset rather than deleted | 
 |         if (fHead == block) { | 
 |             fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + | 
 |                                 kHeaderSize; | 
 |             fHead->fLiveCount = 0; | 
 |             fHead->fFreeSize = fPreallocSize; | 
 |         } else { | 
 |             BlockHeader* prev = block->fPrev; | 
 |             BlockHeader* next = block->fNext; | 
 |             SkASSERT(prev); | 
 |             prev->fNext = next; | 
 |             if (next) { | 
 |                 next->fPrev = prev; | 
 |             } else { | 
 |                 SkASSERT(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; | 
 |         } | 
 |     } | 
 |     SkDEBUGCODE(--fAllocationCnt); | 
 |     VALIDATE; | 
 | } | 
 |  | 
 | GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) { | 
 |     BlockHeader* block = | 
 |         reinterpret_cast<BlockHeader*>(sk_malloc_throw(size + kHeaderSize)); | 
 |     // we assume malloc gives us aligned memory | 
 |     SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment)); | 
 |     block->fLiveCount = 0; | 
 |     block->fFreeSize = size; | 
 |     block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize; | 
 |     block->fPrevPtr = 0; // gcc warns on assigning NULL to an intptr_t. | 
 |     return block; | 
 | } | 
 |  | 
 | void GrMemoryPool::DeleteBlock(BlockHeader* block) { | 
 |     sk_free(block); | 
 | } | 
 |  | 
 | void GrMemoryPool::validate() { | 
 | #ifdef SK_DEBUG | 
 |     BlockHeader* block = fHead; | 
 |     BlockHeader* prev = NULL; | 
 |     SkASSERT(block); | 
 |     int allocCount = 0; | 
 |     do { | 
 |         allocCount += block->fLiveCount; | 
 |         SkASSERT(prev == block->fPrev); | 
 |         if (NULL != prev) { | 
 |             SkASSERT(prev->fNext == block); | 
 |         } | 
 |  | 
 |         intptr_t b = reinterpret_cast<intptr_t>(block); | 
 |         size_t ptrOffset = block->fCurrPtr - b; | 
 |         size_t totalSize = ptrOffset + block->fFreeSize; | 
 |         size_t userSize = totalSize - kHeaderSize; | 
 |         intptr_t userStart = b + kHeaderSize; | 
 |  | 
 |         SkASSERT(!(b % kAlignment)); | 
 |         SkASSERT(!(totalSize % kAlignment)); | 
 |         SkASSERT(!(userSize % kAlignment)); | 
 |         SkASSERT(!(block->fCurrPtr % kAlignment)); | 
 |         if (fHead != block) { | 
 |             SkASSERT(block->fLiveCount); | 
 |             SkASSERT(userSize >= fMinAllocSize); | 
 |         } else { | 
 |             SkASSERT(userSize == fPreallocSize); | 
 |         } | 
 |         if (!block->fLiveCount) { | 
 |             SkASSERT(ptrOffset ==  kHeaderSize); | 
 |             SkASSERT(userStart == block->fCurrPtr); | 
 |         } else { | 
 |             SkASSERT(block == *reinterpret_cast<BlockHeader**>(userStart)); | 
 |         } | 
 |         prev = block; | 
 |     } while ((block = block->fNext)); | 
 |     SkASSERT(allocCount == fAllocationCnt); | 
 |     SkASSERT(prev == fTail); | 
 | #endif | 
 | } |