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