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