/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkChunkAlloc.h" // Don't malloc any chunks smaller than this #define MIN_CHUNKALLOC_BLOCK_SIZE 1024 // Return the new min blocksize given the current value static size_t increase_next_size(size_t size) { return size + (size >> 1); } /////////////////////////////////////////////////////////////////////////////// struct SkChunkAlloc::Block { Block* fNext; size_t fFreeSize; char* fFreePtr; // data[] follows size_t blockSize() { char* start = this->startOfData(); size_t bytes = fFreePtr - start; return fFreeSize + bytes; } void reset() { fNext = nullptr; fFreeSize = this->blockSize(); fFreePtr = this->startOfData(); } char* startOfData() { return reinterpret_cast<char*>(this + 1); } static void FreeChain(Block* block) { while (block) { Block* next = block->fNext; sk_free(block); block = next; } }; bool contains(const void* addr) const { const char* ptr = reinterpret_cast<const char*>(addr); return ptr >= (const char*)(this + 1) && ptr < fFreePtr; } }; /////////////////////////////////////////////////////////////////////////////// SkChunkAlloc::SkChunkAlloc(size_t minSize) { if (minSize < MIN_CHUNKALLOC_BLOCK_SIZE) { minSize = MIN_CHUNKALLOC_BLOCK_SIZE; } fBlock = nullptr; fMinSize = minSize; fChunkSize = fMinSize; fTotalCapacity = 0; fTotalUsed = 0; SkDEBUGCODE(fTotalLost = 0;) SkDEBUGCODE(fBlockCount = 0;) } SkChunkAlloc::~SkChunkAlloc() { this->reset(); } void SkChunkAlloc::reset() { Block::FreeChain(fBlock); fBlock = nullptr; fChunkSize = fMinSize; // reset to our initial minSize fTotalCapacity = 0; fTotalUsed = 0; SkDEBUGCODE(fTotalLost = 0;) SkDEBUGCODE(fBlockCount = 0;) } void SkChunkAlloc::rewind() { SkDEBUGCODE(this->validate();) Block* largest = fBlock; if (largest) { Block* next; for (Block* cur = largest->fNext; cur; cur = next) { next = cur->fNext; if (cur->blockSize() > largest->blockSize()) { sk_free(largest); largest = cur; } else { sk_free(cur); } } largest->reset(); fTotalCapacity = largest->blockSize(); SkDEBUGCODE(fBlockCount = 1;) } else { fTotalCapacity = 0; SkDEBUGCODE(fBlockCount = 0;) } fBlock = largest; fChunkSize = fMinSize; // reset to our initial minSize fTotalUsed = 0; SkDEBUGCODE(fTotalLost = 0;) SkDEBUGCODE(this->validate();) } SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) { size_t size = bytes; if (size < fChunkSize) { size = fChunkSize; } Block* block = (Block*)sk_malloc_flags(sizeof(Block) + size, ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0); if (block) { block->fFreeSize = size; block->fFreePtr = block->startOfData(); fTotalCapacity += size; SkDEBUGCODE(fBlockCount += 1;) fChunkSize = increase_next_size(fChunkSize); } return block; } SkChunkAlloc::Block* SkChunkAlloc::addBlockIfNecessary(size_t bytes, AllocFailType ftype) { SkASSERT(SkIsAlign4(bytes)); if (!fBlock || bytes > fBlock->fFreeSize) { Block* block = this->newBlock(bytes, ftype); if (!block) { return nullptr; } #ifdef SK_DEBUG if (fBlock) { fTotalLost += fBlock->fFreeSize; } #endif block->fNext = fBlock; fBlock = block; } SkASSERT(fBlock && bytes <= fBlock->fFreeSize); return fBlock; } void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) { SkDEBUGCODE(this->validate();) bytes = SkAlign4(bytes); Block* block = this->addBlockIfNecessary(bytes, ftype); if (!block) { return nullptr; } char* ptr = block->fFreePtr; fTotalUsed += bytes; block->fFreeSize -= bytes; block->fFreePtr = ptr + bytes; SkDEBUGCODE(this->validate();) return ptr; } size_t SkChunkAlloc::unalloc(void* ptr) { SkDEBUGCODE(this->validate();) size_t bytes = 0; Block* block = fBlock; if (block) { char* cPtr = reinterpret_cast<char*>(ptr); char* start = block->startOfData(); if (start <= cPtr && cPtr < block->fFreePtr) { bytes = block->fFreePtr - cPtr; fTotalUsed -= bytes; block->fFreeSize += bytes; block->fFreePtr = cPtr; } } SkDEBUGCODE(this->validate();) return bytes; } bool SkChunkAlloc::contains(const void* addr) const { const Block* block = fBlock; while (block) { if (block->contains(addr)) { return true; } block = block->fNext; } return false; } #ifdef SK_DEBUG void SkChunkAlloc::validate() { int numBlocks = 0; size_t totCapacity = 0; size_t totUsed = 0; size_t totLost = 0; size_t totAvailable = 0; for (Block* temp = fBlock; temp; temp = temp->fNext) { ++numBlocks; totCapacity += temp->blockSize(); totUsed += temp->fFreePtr - temp->startOfData(); if (temp == fBlock) { totAvailable += temp->fFreeSize; } else { totLost += temp->fFreeSize; } } SkASSERT(fBlockCount == numBlocks); SkASSERT(fTotalCapacity == totCapacity); SkASSERT(fTotalUsed == totUsed); SkASSERT(fTotalLost == totLost); SkASSERT(totCapacity == totUsed + totLost + totAvailable); } #endif