1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/core/SkBlockAllocator.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#ifdef SK_DEBUG 11cb93a386Sopenharmony_ci#include <vector> 12cb93a386Sopenharmony_ci#endif 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ciSkBlockAllocator::SkBlockAllocator(GrowthPolicy policy, size_t blockIncrementBytes, 15cb93a386Sopenharmony_ci size_t additionalPreallocBytes) 16cb93a386Sopenharmony_ci : fTail(&fHead) 17cb93a386Sopenharmony_ci // Round up to the nearest max-aligned value, and then divide so that fBlockSizeIncrement 18cb93a386Sopenharmony_ci // can effectively fit higher byte counts in its 16 bits of storage 19cb93a386Sopenharmony_ci , fBlockIncrement(SkTo<uint16_t>(SkAlignTo(blockIncrementBytes, kAddressAlign) 20cb93a386Sopenharmony_ci / kAddressAlign)) 21cb93a386Sopenharmony_ci , fGrowthPolicy(static_cast<uint64_t>(policy)) 22cb93a386Sopenharmony_ci , fN0((policy == GrowthPolicy::kLinear || policy == GrowthPolicy::kExponential) ? 1 : 0) 23cb93a386Sopenharmony_ci , fN1(1) 24cb93a386Sopenharmony_ci // The head block always fills remaining space from SkBlockAllocator's size, because it's 25cb93a386Sopenharmony_ci // inline, but can take over the specified number of bytes immediately after it. 26cb93a386Sopenharmony_ci , fHead(/*prev=*/nullptr, additionalPreallocBytes + BaseHeadBlockSize()) { 27cb93a386Sopenharmony_ci SkASSERT(fBlockIncrement >= 1); 28cb93a386Sopenharmony_ci SkASSERT(additionalPreallocBytes <= kMaxAllocationSize); 29cb93a386Sopenharmony_ci} 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_ciSkBlockAllocator::Block::Block(Block* prev, int allocationSize) 32cb93a386Sopenharmony_ci : fNext(nullptr) 33cb93a386Sopenharmony_ci , fPrev(prev) 34cb93a386Sopenharmony_ci , fSize(allocationSize) 35cb93a386Sopenharmony_ci , fCursor(kDataStart) 36cb93a386Sopenharmony_ci , fMetadata(0) 37cb93a386Sopenharmony_ci , fAllocatorMetadata(0) { 38cb93a386Sopenharmony_ci SkASSERT(allocationSize >= (int) sizeof(Block)); 39cb93a386Sopenharmony_ci SkDEBUGCODE(fSentinel = kAssignedMarker;) 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci this->poisonRange(kDataStart, fSize); 42cb93a386Sopenharmony_ci} 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ciSkBlockAllocator::Block::~Block() { 45cb93a386Sopenharmony_ci this->unpoisonRange(kDataStart, fSize); 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ci SkASSERT(fSentinel == kAssignedMarker); 48cb93a386Sopenharmony_ci SkDEBUGCODE(fSentinel = kFreedMarker;) // FWIW 49cb93a386Sopenharmony_ci} 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_cisize_t SkBlockAllocator::totalSize() const { 52cb93a386Sopenharmony_ci // Use size_t since the sum across all blocks could exceed 'int', even though each block won't 53cb93a386Sopenharmony_ci size_t size = offsetof(SkBlockAllocator, fHead) + this->scratchBlockSize(); 54cb93a386Sopenharmony_ci for (const Block* b : this->blocks()) { 55cb93a386Sopenharmony_ci size += b->fSize; 56cb93a386Sopenharmony_ci } 57cb93a386Sopenharmony_ci SkASSERT(size >= this->preallocSize()); 58cb93a386Sopenharmony_ci return size; 59cb93a386Sopenharmony_ci} 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_cisize_t SkBlockAllocator::totalUsableSpace() const { 62cb93a386Sopenharmony_ci size_t size = this->scratchBlockSize(); 63cb93a386Sopenharmony_ci if (size > 0) { 64cb93a386Sopenharmony_ci size -= kDataStart; // scratchBlockSize reports total block size, not usable size 65cb93a386Sopenharmony_ci } 66cb93a386Sopenharmony_ci for (const Block* b : this->blocks()) { 67cb93a386Sopenharmony_ci size += (b->fSize - kDataStart); 68cb93a386Sopenharmony_ci } 69cb93a386Sopenharmony_ci SkASSERT(size >= this->preallocUsableSpace()); 70cb93a386Sopenharmony_ci return size; 71cb93a386Sopenharmony_ci} 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_cisize_t SkBlockAllocator::totalSpaceInUse() const { 74cb93a386Sopenharmony_ci size_t size = 0; 75cb93a386Sopenharmony_ci for (const Block* b : this->blocks()) { 76cb93a386Sopenharmony_ci size += (b->fCursor - kDataStart); 77cb93a386Sopenharmony_ci } 78cb93a386Sopenharmony_ci SkASSERT(size <= this->totalUsableSpace()); 79cb93a386Sopenharmony_ci return size; 80cb93a386Sopenharmony_ci} 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ciSkBlockAllocator::Block* SkBlockAllocator::findOwningBlock(const void* p) { 83cb93a386Sopenharmony_ci // When in doubt, search in reverse to find an overlapping block. 84cb93a386Sopenharmony_ci uintptr_t ptr = reinterpret_cast<uintptr_t>(p); 85cb93a386Sopenharmony_ci for (Block* b : this->rblocks()) { 86cb93a386Sopenharmony_ci uintptr_t lowerBound = reinterpret_cast<uintptr_t>(b) + kDataStart; 87cb93a386Sopenharmony_ci uintptr_t upperBound = reinterpret_cast<uintptr_t>(b) + b->fSize; 88cb93a386Sopenharmony_ci if (lowerBound <= ptr && ptr < upperBound) { 89cb93a386Sopenharmony_ci SkASSERT(b->fSentinel == kAssignedMarker); 90cb93a386Sopenharmony_ci return b; 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci } 93cb93a386Sopenharmony_ci return nullptr; 94cb93a386Sopenharmony_ci} 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_civoid SkBlockAllocator::releaseBlock(Block* block) { 97cb93a386Sopenharmony_ci if (block == &fHead) { 98cb93a386Sopenharmony_ci // Reset the cursor of the head block so that it can be reused if it becomes the new tail 99cb93a386Sopenharmony_ci block->fCursor = kDataStart; 100cb93a386Sopenharmony_ci block->fMetadata = 0; 101cb93a386Sopenharmony_ci block->poisonRange(kDataStart, block->fSize); 102cb93a386Sopenharmony_ci // Unlike in reset(), we don't set the head's next block to null because there are 103cb93a386Sopenharmony_ci // potentially heap-allocated blocks that are still connected to it. 104cb93a386Sopenharmony_ci } else { 105cb93a386Sopenharmony_ci SkASSERT(block->fPrev); 106cb93a386Sopenharmony_ci block->fPrev->fNext = block->fNext; 107cb93a386Sopenharmony_ci if (block->fNext) { 108cb93a386Sopenharmony_ci SkASSERT(fTail != block); 109cb93a386Sopenharmony_ci block->fNext->fPrev = block->fPrev; 110cb93a386Sopenharmony_ci } else { 111cb93a386Sopenharmony_ci SkASSERT(fTail == block); 112cb93a386Sopenharmony_ci fTail = block->fPrev; 113cb93a386Sopenharmony_ci } 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ci // The released block becomes the new scratch block (if it's bigger), or delete it 116cb93a386Sopenharmony_ci if (this->scratchBlockSize() < block->fSize) { 117cb93a386Sopenharmony_ci SkASSERT(block != fHead.fPrev); // shouldn't already be the scratch block 118cb93a386Sopenharmony_ci if (fHead.fPrev) { 119cb93a386Sopenharmony_ci delete fHead.fPrev; 120cb93a386Sopenharmony_ci } 121cb93a386Sopenharmony_ci block->markAsScratch(); 122cb93a386Sopenharmony_ci fHead.fPrev = block; 123cb93a386Sopenharmony_ci } else { 124cb93a386Sopenharmony_ci delete block; 125cb93a386Sopenharmony_ci } 126cb93a386Sopenharmony_ci } 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_ci // Decrement growth policy (opposite of addBlock()'s increment operations) 129cb93a386Sopenharmony_ci GrowthPolicy gp = static_cast<GrowthPolicy>(fGrowthPolicy); 130cb93a386Sopenharmony_ci if (fN0 > 0 && (fN1 > 1 || gp == GrowthPolicy::kFibonacci)) { 131cb93a386Sopenharmony_ci SkASSERT(gp != GrowthPolicy::kFixed); // fixed never needs undoing, fN0 always is 0 132cb93a386Sopenharmony_ci if (gp == GrowthPolicy::kLinear) { 133cb93a386Sopenharmony_ci fN1 = fN1 - fN0; 134cb93a386Sopenharmony_ci } else if (gp == GrowthPolicy::kFibonacci) { 135cb93a386Sopenharmony_ci // Subtract n0 from n1 to get the prior 2 terms in the fibonacci sequence 136cb93a386Sopenharmony_ci int temp = fN1 - fN0; // yields prior fN0 137cb93a386Sopenharmony_ci fN1 = fN1 - temp; // yields prior fN1 138cb93a386Sopenharmony_ci fN0 = temp; 139cb93a386Sopenharmony_ci } else { 140cb93a386Sopenharmony_ci SkASSERT(gp == GrowthPolicy::kExponential); 141cb93a386Sopenharmony_ci // Divide by 2 to undo the 2N update from addBlock 142cb93a386Sopenharmony_ci fN1 = fN1 >> 1; 143cb93a386Sopenharmony_ci fN0 = fN1; 144cb93a386Sopenharmony_ci } 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci 147cb93a386Sopenharmony_ci SkASSERT(fN1 >= 1 && fN0 >= 0); 148cb93a386Sopenharmony_ci} 149cb93a386Sopenharmony_ci 150cb93a386Sopenharmony_civoid SkBlockAllocator::stealHeapBlocks(SkBlockAllocator* other) { 151cb93a386Sopenharmony_ci Block* toSteal = other->fHead.fNext; 152cb93a386Sopenharmony_ci if (toSteal) { 153cb93a386Sopenharmony_ci // The other's next block connects back to this allocator's current tail, and its new tail 154cb93a386Sopenharmony_ci // becomes the end of other's block linked list. 155cb93a386Sopenharmony_ci SkASSERT(other->fTail != &other->fHead); 156cb93a386Sopenharmony_ci toSteal->fPrev = fTail; 157cb93a386Sopenharmony_ci fTail->fNext = toSteal; 158cb93a386Sopenharmony_ci fTail = other->fTail; 159cb93a386Sopenharmony_ci // The other allocator becomes just its inline head block 160cb93a386Sopenharmony_ci other->fTail = &other->fHead; 161cb93a386Sopenharmony_ci other->fHead.fNext = nullptr; 162cb93a386Sopenharmony_ci } // else no block to steal 163cb93a386Sopenharmony_ci} 164cb93a386Sopenharmony_ci 165cb93a386Sopenharmony_civoid SkBlockAllocator::reset() { 166cb93a386Sopenharmony_ci for (Block* b : this->rblocks()) { 167cb93a386Sopenharmony_ci if (b == &fHead) { 168cb93a386Sopenharmony_ci // Reset metadata and cursor, tail points to the head block again 169cb93a386Sopenharmony_ci fTail = b; 170cb93a386Sopenharmony_ci b->fNext = nullptr; 171cb93a386Sopenharmony_ci b->fCursor = kDataStart; 172cb93a386Sopenharmony_ci b->fMetadata = 0; 173cb93a386Sopenharmony_ci // For reset(), but NOT releaseBlock(), the head allocatorMetadata and scratch block 174cb93a386Sopenharmony_ci // are reset/destroyed. 175cb93a386Sopenharmony_ci b->fAllocatorMetadata = 0; 176cb93a386Sopenharmony_ci b->poisonRange(kDataStart, b->fSize); 177cb93a386Sopenharmony_ci this->resetScratchSpace(); 178cb93a386Sopenharmony_ci } else { 179cb93a386Sopenharmony_ci delete b; 180cb93a386Sopenharmony_ci } 181cb93a386Sopenharmony_ci } 182cb93a386Sopenharmony_ci SkASSERT(fTail == &fHead && fHead.fNext == nullptr && fHead.fPrev == nullptr && 183cb93a386Sopenharmony_ci fHead.metadata() == 0 && fHead.fCursor == kDataStart); 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ci GrowthPolicy gp = static_cast<GrowthPolicy>(fGrowthPolicy); 186cb93a386Sopenharmony_ci fN0 = (gp == GrowthPolicy::kLinear || gp == GrowthPolicy::kExponential) ? 1 : 0; 187cb93a386Sopenharmony_ci fN1 = 1; 188cb93a386Sopenharmony_ci} 189cb93a386Sopenharmony_ci 190cb93a386Sopenharmony_civoid SkBlockAllocator::resetScratchSpace() { 191cb93a386Sopenharmony_ci if (fHead.fPrev) { 192cb93a386Sopenharmony_ci delete fHead.fPrev; 193cb93a386Sopenharmony_ci fHead.fPrev = nullptr; 194cb93a386Sopenharmony_ci } 195cb93a386Sopenharmony_ci} 196cb93a386Sopenharmony_ci 197cb93a386Sopenharmony_civoid SkBlockAllocator::addBlock(int minSize, int maxSize) { 198cb93a386Sopenharmony_ci SkASSERT(minSize > (int) sizeof(Block) && minSize <= maxSize); 199cb93a386Sopenharmony_ci 200cb93a386Sopenharmony_ci // Max positive value for uint:23 storage (decltype(fN0) picks up uint64_t, not uint:23). 201cb93a386Sopenharmony_ci static constexpr int kMaxN = (1 << 23) - 1; 202cb93a386Sopenharmony_ci static_assert(2 * kMaxN <= std::numeric_limits<int32_t>::max()); // Growth policy won't overflow 203cb93a386Sopenharmony_ci 204cb93a386Sopenharmony_ci auto alignAllocSize = [](int size) { 205cb93a386Sopenharmony_ci // Round to a nice boundary since the block isn't maxing out: 206cb93a386Sopenharmony_ci // if allocSize > 32K, aligns on 4K boundary otherwise aligns on max_align_t, to play 207cb93a386Sopenharmony_ci // nicely with jeMalloc (from SkArenaAlloc). 208cb93a386Sopenharmony_ci int mask = size > (1 << 15) ? ((1 << 12) - 1) : (kAddressAlign - 1); 209cb93a386Sopenharmony_ci return (size + mask) & ~mask; 210cb93a386Sopenharmony_ci }; 211cb93a386Sopenharmony_ci 212cb93a386Sopenharmony_ci int allocSize; 213cb93a386Sopenharmony_ci void* mem = nullptr; 214cb93a386Sopenharmony_ci if (this->scratchBlockSize() >= minSize) { 215cb93a386Sopenharmony_ci // Activate the scratch block instead of making a new block 216cb93a386Sopenharmony_ci SkASSERT(fHead.fPrev->isScratch()); 217cb93a386Sopenharmony_ci allocSize = fHead.fPrev->fSize; 218cb93a386Sopenharmony_ci mem = fHead.fPrev; 219cb93a386Sopenharmony_ci fHead.fPrev = nullptr; 220cb93a386Sopenharmony_ci } else if (minSize < maxSize) { 221cb93a386Sopenharmony_ci // Calculate the 'next' size per growth policy sequence 222cb93a386Sopenharmony_ci GrowthPolicy gp = static_cast<GrowthPolicy>(fGrowthPolicy); 223cb93a386Sopenharmony_ci int nextN1 = fN0 + fN1; 224cb93a386Sopenharmony_ci int nextN0; 225cb93a386Sopenharmony_ci if (gp == GrowthPolicy::kFixed || gp == GrowthPolicy::kLinear) { 226cb93a386Sopenharmony_ci nextN0 = fN0; 227cb93a386Sopenharmony_ci } else if (gp == GrowthPolicy::kFibonacci) { 228cb93a386Sopenharmony_ci nextN0 = fN1; 229cb93a386Sopenharmony_ci } else { 230cb93a386Sopenharmony_ci SkASSERT(gp == GrowthPolicy::kExponential); 231cb93a386Sopenharmony_ci nextN0 = nextN1; 232cb93a386Sopenharmony_ci } 233cb93a386Sopenharmony_ci fN0 = std::min(kMaxN, nextN0); 234cb93a386Sopenharmony_ci fN1 = std::min(kMaxN, nextN1); 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci // However, must guard against overflow here, since all the size-based asserts prevented 237cb93a386Sopenharmony_ci // alignment/addition overflows, while multiplication requires 2x bits instead of x+1. 238cb93a386Sopenharmony_ci int sizeIncrement = fBlockIncrement * kAddressAlign; 239cb93a386Sopenharmony_ci if (maxSize / sizeIncrement < nextN1) { 240cb93a386Sopenharmony_ci // The growth policy would overflow, so use the max. We've already confirmed that 241cb93a386Sopenharmony_ci // maxSize will be sufficient for the requested minimumSize 242cb93a386Sopenharmony_ci allocSize = maxSize; 243cb93a386Sopenharmony_ci } else { 244cb93a386Sopenharmony_ci allocSize = std::min(alignAllocSize(std::max(minSize, sizeIncrement * nextN1)), 245cb93a386Sopenharmony_ci maxSize); 246cb93a386Sopenharmony_ci } 247cb93a386Sopenharmony_ci } else { 248cb93a386Sopenharmony_ci SkASSERT(minSize == maxSize); 249cb93a386Sopenharmony_ci // Still align on a nice boundary, no max clamping since that would just undo the alignment 250cb93a386Sopenharmony_ci allocSize = alignAllocSize(minSize); 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci // Create new block and append to the linked list of blocks in this allocator 254cb93a386Sopenharmony_ci if (!mem) { 255cb93a386Sopenharmony_ci mem = operator new(allocSize); 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci fTail->fNext = new (mem) Block(fTail, allocSize); 258cb93a386Sopenharmony_ci fTail = fTail->fNext; 259cb93a386Sopenharmony_ci} 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci#ifdef SK_DEBUG 262cb93a386Sopenharmony_civoid SkBlockAllocator::validate() const { 263cb93a386Sopenharmony_ci std::vector<const Block*> blocks; 264cb93a386Sopenharmony_ci const Block* prev = nullptr; 265cb93a386Sopenharmony_ci for (const Block* block : this->blocks()) { 266cb93a386Sopenharmony_ci blocks.push_back(block); 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ci SkASSERT(kAssignedMarker == block->fSentinel); 269cb93a386Sopenharmony_ci if (block == &fHead) { 270cb93a386Sopenharmony_ci // The head blocks' fPrev may be non-null if it holds a scratch block, but that's not 271cb93a386Sopenharmony_ci // considered part of the linked list 272cb93a386Sopenharmony_ci SkASSERT(!prev && (!fHead.fPrev || fHead.fPrev->isScratch())); 273cb93a386Sopenharmony_ci } else { 274cb93a386Sopenharmony_ci SkASSERT(prev == block->fPrev); 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci if (prev) { 277cb93a386Sopenharmony_ci SkASSERT(prev->fNext == block); 278cb93a386Sopenharmony_ci } 279cb93a386Sopenharmony_ci 280cb93a386Sopenharmony_ci SkASSERT(block->fSize >= (int) sizeof(Block)); 281cb93a386Sopenharmony_ci SkASSERT(block->fCursor >= kDataStart); 282cb93a386Sopenharmony_ci SkASSERT(block->fCursor <= block->fSize); 283cb93a386Sopenharmony_ci 284cb93a386Sopenharmony_ci prev = block; 285cb93a386Sopenharmony_ci } 286cb93a386Sopenharmony_ci SkASSERT(prev == fTail); 287cb93a386Sopenharmony_ci SkASSERT(!blocks.empty()); 288cb93a386Sopenharmony_ci SkASSERT(blocks[0] == &fHead); 289cb93a386Sopenharmony_ci 290cb93a386Sopenharmony_ci // Confirm reverse iteration matches forward iteration 291cb93a386Sopenharmony_ci size_t j = blocks.size(); 292cb93a386Sopenharmony_ci for (const Block* b : this->rblocks()) { 293cb93a386Sopenharmony_ci SkASSERT(b == blocks[j - 1]); 294cb93a386Sopenharmony_ci j--; 295cb93a386Sopenharmony_ci } 296cb93a386Sopenharmony_ci SkASSERT(j == 0); 297cb93a386Sopenharmony_ci} 298cb93a386Sopenharmony_ci#endif 299