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