xref: /aosp_15_r20/external/skia/src/base/SkArenaAlloc.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2016 Google Inc.
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/SkArenaAlloc.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
11*c8dee2aaSAndroid Build Coastguard Worker 
12*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
13*c8dee2aaSAndroid Build Coastguard Worker #include <cassert>
14*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
15*c8dee2aaSAndroid Build Coastguard Worker 
end_chain(char *)16*c8dee2aaSAndroid Build Coastguard Worker static char* end_chain(char*) { return nullptr; }
17*c8dee2aaSAndroid Build Coastguard Worker 
SkArenaAlloc(char * block,size_t size,size_t firstHeapAllocation)18*c8dee2aaSAndroid Build Coastguard Worker SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t firstHeapAllocation)
19*c8dee2aaSAndroid Build Coastguard Worker     : fDtorCursor {block}
20*c8dee2aaSAndroid Build Coastguard Worker     , fCursor     {block}
21*c8dee2aaSAndroid Build Coastguard Worker     , fEnd        {block + SkToU32(size)}
22*c8dee2aaSAndroid Build Coastguard Worker     , fFibonacciProgression{SkToU32(size), SkToU32(firstHeapAllocation)}
23*c8dee2aaSAndroid Build Coastguard Worker {
24*c8dee2aaSAndroid Build Coastguard Worker     if (size < sizeof(Footer)) {
25*c8dee2aaSAndroid Build Coastguard Worker         fEnd = fCursor = fDtorCursor = nullptr;
26*c8dee2aaSAndroid Build Coastguard Worker     }
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker     if (fCursor != nullptr) {
29*c8dee2aaSAndroid Build Coastguard Worker         this->installFooter(end_chain, 0);
30*c8dee2aaSAndroid Build Coastguard Worker         sk_asan_poison_memory_region(fCursor, fEnd - fCursor);
31*c8dee2aaSAndroid Build Coastguard Worker     }
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker 
~SkArenaAlloc()34*c8dee2aaSAndroid Build Coastguard Worker SkArenaAlloc::~SkArenaAlloc() {
35*c8dee2aaSAndroid Build Coastguard Worker     RunDtorsOnBlock(fDtorCursor);
36*c8dee2aaSAndroid Build Coastguard Worker }
37*c8dee2aaSAndroid Build Coastguard Worker 
installFooter(FooterAction * action,uint32_t padding)38*c8dee2aaSAndroid Build Coastguard Worker void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
39*c8dee2aaSAndroid Build Coastguard Worker     assert(SkTFitsIn<uint8_t>(padding));
40*c8dee2aaSAndroid Build Coastguard Worker     this->installRaw(action);
41*c8dee2aaSAndroid Build Coastguard Worker     this->installRaw((uint8_t)padding);
42*c8dee2aaSAndroid Build Coastguard Worker     fDtorCursor = fCursor;
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker 
SkipPod(char * footerEnd)45*c8dee2aaSAndroid Build Coastguard Worker char* SkArenaAlloc::SkipPod(char* footerEnd) {
46*c8dee2aaSAndroid Build Coastguard Worker     char* objEnd = footerEnd - (sizeof(Footer) + sizeof(uint32_t));
47*c8dee2aaSAndroid Build Coastguard Worker     uint32_t skip;
48*c8dee2aaSAndroid Build Coastguard Worker     memmove(&skip, objEnd, sizeof(uint32_t));
49*c8dee2aaSAndroid Build Coastguard Worker     return objEnd - (ptrdiff_t) skip;
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker 
RunDtorsOnBlock(char * footerEnd)52*c8dee2aaSAndroid Build Coastguard Worker void SkArenaAlloc::RunDtorsOnBlock(char* footerEnd) {
53*c8dee2aaSAndroid Build Coastguard Worker     while (footerEnd != nullptr) {
54*c8dee2aaSAndroid Build Coastguard Worker         FooterAction* action;
55*c8dee2aaSAndroid Build Coastguard Worker         uint8_t       padding;
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker         memcpy(&action,  footerEnd - sizeof( Footer), sizeof( action));
58*c8dee2aaSAndroid Build Coastguard Worker         memcpy(&padding, footerEnd - sizeof(padding), sizeof(padding));
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker         footerEnd = action(footerEnd) - (ptrdiff_t)padding;
61*c8dee2aaSAndroid Build Coastguard Worker     }
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker 
NextBlock(char * footerEnd)64*c8dee2aaSAndroid Build Coastguard Worker char* SkArenaAlloc::NextBlock(char* footerEnd) {
65*c8dee2aaSAndroid Build Coastguard Worker     char* objEnd = footerEnd - (sizeof(char*) + sizeof(Footer));
66*c8dee2aaSAndroid Build Coastguard Worker     char* next;
67*c8dee2aaSAndroid Build Coastguard Worker     memmove(&next, objEnd, sizeof(char*));
68*c8dee2aaSAndroid Build Coastguard Worker     RunDtorsOnBlock(next);
69*c8dee2aaSAndroid Build Coastguard Worker     sk_free(objEnd);
70*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
71*c8dee2aaSAndroid Build Coastguard Worker }
72*c8dee2aaSAndroid Build Coastguard Worker 
ensureSpace(uint32_t size,uint32_t alignment)73*c8dee2aaSAndroid Build Coastguard Worker void SkArenaAlloc::ensureSpace(uint32_t size, uint32_t alignment) {
74*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t headerSize = sizeof(Footer) + sizeof(ptrdiff_t);
75*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t maxSize = std::numeric_limits<uint32_t>::max();
76*c8dee2aaSAndroid Build Coastguard Worker     constexpr uint32_t overhead = headerSize + sizeof(Footer);
77*c8dee2aaSAndroid Build Coastguard Worker     AssertRelease(size <= maxSize - overhead);
78*c8dee2aaSAndroid Build Coastguard Worker     uint32_t objSizeAndOverhead = size + overhead;
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t alignmentOverhead = alignment - 1;
81*c8dee2aaSAndroid Build Coastguard Worker     AssertRelease(objSizeAndOverhead <= maxSize - alignmentOverhead);
82*c8dee2aaSAndroid Build Coastguard Worker     objSizeAndOverhead += alignmentOverhead;
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker     uint32_t minAllocationSize = fFibonacciProgression.nextBlockSize();
85*c8dee2aaSAndroid Build Coastguard Worker     uint32_t allocationSize = std::max(objSizeAndOverhead, minAllocationSize);
86*c8dee2aaSAndroid Build Coastguard Worker 
87*c8dee2aaSAndroid Build Coastguard Worker     // Round up to a nice size. If > 32K align to 4K boundary else up to max_align_t. The > 32K
88*c8dee2aaSAndroid Build Coastguard Worker     // heuristic is from the JEMalloc behavior.
89*c8dee2aaSAndroid Build Coastguard Worker     {
90*c8dee2aaSAndroid Build Coastguard Worker         uint32_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 16 - 1;
91*c8dee2aaSAndroid Build Coastguard Worker         AssertRelease(allocationSize <= maxSize - mask);
92*c8dee2aaSAndroid Build Coastguard Worker         allocationSize = (allocationSize + mask) & ~mask;
93*c8dee2aaSAndroid Build Coastguard Worker     }
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker     char* newBlock = static_cast<char*>(sk_malloc_throw(allocationSize));
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker     auto previousDtor = fDtorCursor;
98*c8dee2aaSAndroid Build Coastguard Worker     fCursor = newBlock;
99*c8dee2aaSAndroid Build Coastguard Worker     fDtorCursor = newBlock;
100*c8dee2aaSAndroid Build Coastguard Worker     fEnd = fCursor + allocationSize;
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker     // poison the unused bytes in the block.
103*c8dee2aaSAndroid Build Coastguard Worker     sk_asan_poison_memory_region(fCursor, fEnd - fCursor);
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker     this->installRaw(previousDtor);
106*c8dee2aaSAndroid Build Coastguard Worker     this->installFooter(NextBlock, 0);
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker 
allocObjectWithFooter(uint32_t sizeIncludingFooter,uint32_t alignment)109*c8dee2aaSAndroid Build Coastguard Worker char* SkArenaAlloc::allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment) {
110*c8dee2aaSAndroid Build Coastguard Worker     uintptr_t mask = alignment - 1;
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker restart:
113*c8dee2aaSAndroid Build Coastguard Worker     uint32_t skipOverhead = 0;
114*c8dee2aaSAndroid Build Coastguard Worker     const bool needsSkipFooter = fCursor != fDtorCursor;
115*c8dee2aaSAndroid Build Coastguard Worker     if (needsSkipFooter) {
116*c8dee2aaSAndroid Build Coastguard Worker         skipOverhead = sizeof(Footer) + sizeof(uint32_t);
117*c8dee2aaSAndroid Build Coastguard Worker     }
118*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t totalSize = sizeIncludingFooter + skipOverhead;
119*c8dee2aaSAndroid Build Coastguard Worker 
120*c8dee2aaSAndroid Build Coastguard Worker     // Math on null fCursor/fEnd is undefined behavior, so explicitly check for first alloc.
121*c8dee2aaSAndroid Build Coastguard Worker     if (!fCursor) {
122*c8dee2aaSAndroid Build Coastguard Worker         this->ensureSpace(totalSize, alignment);
123*c8dee2aaSAndroid Build Coastguard Worker         goto restart;
124*c8dee2aaSAndroid Build Coastguard Worker     }
125*c8dee2aaSAndroid Build Coastguard Worker 
126*c8dee2aaSAndroid Build Coastguard Worker     assert(fEnd);
127*c8dee2aaSAndroid Build Coastguard Worker     // This test alone would be enough nullptr were defined to be 0, but it's not.
128*c8dee2aaSAndroid Build Coastguard Worker     char* objStart = (char*)((uintptr_t)(fCursor + skipOverhead + mask) & ~mask);
129*c8dee2aaSAndroid Build Coastguard Worker     if ((ptrdiff_t)totalSize > fEnd - objStart) {
130*c8dee2aaSAndroid Build Coastguard Worker         this->ensureSpace(totalSize, alignment);
131*c8dee2aaSAndroid Build Coastguard Worker         goto restart;
132*c8dee2aaSAndroid Build Coastguard Worker     }
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker     AssertRelease((ptrdiff_t)totalSize <= fEnd - objStart);
135*c8dee2aaSAndroid Build Coastguard Worker 
136*c8dee2aaSAndroid Build Coastguard Worker     // Install a skip footer if needed, thus terminating a run of POD data. The calling code is
137*c8dee2aaSAndroid Build Coastguard Worker     // responsible for installing the footer after the object.
138*c8dee2aaSAndroid Build Coastguard Worker     if (needsSkipFooter) {
139*c8dee2aaSAndroid Build Coastguard Worker         this->installRaw(SkToU32(fCursor - fDtorCursor));
140*c8dee2aaSAndroid Build Coastguard Worker         this->installFooter(SkipPod, 0);
141*c8dee2aaSAndroid Build Coastguard Worker     }
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker     return objStart;
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker 
SkArenaAllocWithReset(char * block,size_t size,size_t firstHeapAllocation)146*c8dee2aaSAndroid Build Coastguard Worker SkArenaAllocWithReset::SkArenaAllocWithReset(char* block,
147*c8dee2aaSAndroid Build Coastguard Worker                                              size_t size,
148*c8dee2aaSAndroid Build Coastguard Worker                                              size_t firstHeapAllocation)
149*c8dee2aaSAndroid Build Coastguard Worker         : SkArenaAlloc(block, size, firstHeapAllocation)
150*c8dee2aaSAndroid Build Coastguard Worker         , fFirstBlock{block}
151*c8dee2aaSAndroid Build Coastguard Worker         , fFirstSize{SkToU32(size)}
152*c8dee2aaSAndroid Build Coastguard Worker         , fFirstHeapAllocationSize{SkToU32(firstHeapAllocation)} {}
153*c8dee2aaSAndroid Build Coastguard Worker 
reset()154*c8dee2aaSAndroid Build Coastguard Worker void SkArenaAllocWithReset::reset() {
155*c8dee2aaSAndroid Build Coastguard Worker     char* const    firstBlock              = fFirstBlock;
156*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t firstSize               = fFirstSize;
157*c8dee2aaSAndroid Build Coastguard Worker     const uint32_t firstHeapAllocationSize = fFirstHeapAllocationSize;
158*c8dee2aaSAndroid Build Coastguard Worker     this->~SkArenaAllocWithReset();
159*c8dee2aaSAndroid Build Coastguard Worker     new (this) SkArenaAllocWithReset{firstBlock, firstSize, firstHeapAllocationSize};
160*c8dee2aaSAndroid Build Coastguard Worker }
161*c8dee2aaSAndroid Build Coastguard Worker 
isEmpty()162*c8dee2aaSAndroid Build Coastguard Worker bool SkArenaAllocWithReset::isEmpty() {
163*c8dee2aaSAndroid Build Coastguard Worker     return this->cursor() == nullptr ||
164*c8dee2aaSAndroid Build Coastguard Worker            this->cursor() == fFirstBlock + sizeof(Footer);
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker 
167*c8dee2aaSAndroid Build Coastguard Worker // SkFibonacci47 is the first 47 Fibonacci numbers. Fib(47) is the largest value less than 2 ^ 32.
168*c8dee2aaSAndroid Build Coastguard Worker // Used by SkFibBlockSizes.
169*c8dee2aaSAndroid Build Coastguard Worker std::array<const uint32_t, 47> SkFibonacci47 {
170*c8dee2aaSAndroid Build Coastguard Worker                 1,         1,          2,          3,          5,          8,
171*c8dee2aaSAndroid Build Coastguard Worker                13,        21,         34,         55,         89,        144,
172*c8dee2aaSAndroid Build Coastguard Worker               233,       377,        610,        987,       1597,       2584,
173*c8dee2aaSAndroid Build Coastguard Worker              4181,      6765,      10946,      17711,      28657,      46368,
174*c8dee2aaSAndroid Build Coastguard Worker             75025,    121393,     196418,     317811,     514229,     832040,
175*c8dee2aaSAndroid Build Coastguard Worker           1346269,   2178309,    3524578,    5702887,    9227465,   14930352,
176*c8dee2aaSAndroid Build Coastguard Worker          24157817,  39088169,   63245986,  102334155,  165580141,  267914296,
177*c8dee2aaSAndroid Build Coastguard Worker         433494437, 701408733, 1134903170, 1836311903, 2971215073,
178*c8dee2aaSAndroid Build Coastguard Worker };
179