96fcdcc219
DOCS_PREVIEW= https://skia.org/?cl=1316233002 Review URL: https://codereview.chromium.org/1316233002
236 lines
5.7 KiB
C++
236 lines
5.7 KiB
C++
/*
|
|
* 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
|
|
|