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