Line data Source code
1 : /*
2 : * Copyright 2016 Google Inc.
3 : *
4 : * Use of this source code is governed by a BSD-style license that can be
5 : * found in the LICENSE file.
6 : */
7 :
8 : #include <algorithm>
9 : #include <cstddef>
10 : #include "SkArenaAlloc.h"
11 :
12 708 : static char* end_chain(char*) { return nullptr; }
13 :
14 0 : char* SkArenaAlloc::SkipPod(char* footerEnd) {
15 0 : char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
16 : int32_t skip;
17 0 : memmove(&skip, objEnd, sizeof(int32_t));
18 0 : return objEnd - skip;
19 : }
20 :
21 3367 : void SkArenaAlloc::RunDtorsOnBlock(char* footerEnd) {
22 5348 : while (footerEnd != nullptr) {
23 : Footer footer;
24 1981 : memcpy(&footer, footerEnd - sizeof(Footer), sizeof(Footer));
25 :
26 1981 : FooterAction* action = (FooterAction*)(footer >> 6);
27 1981 : ptrdiff_t padding = footer & 63;
28 :
29 1981 : footerEnd = action(footerEnd) - padding;
30 : }
31 1386 : }
32 :
33 226 : char* SkArenaAlloc::NextBlock(char* footerEnd) {
34 226 : char* objEnd = footerEnd - (sizeof(Footer) + sizeof(char*));
35 : char* next;
36 226 : memmove(&next, objEnd, sizeof(char*));
37 226 : RunDtorsOnBlock(next);
38 226 : delete [] objEnd;
39 226 : return nullptr;
40 : }
41 :
42 1162 : SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize)
43 : : fDtorCursor {block}
44 : , fCursor {block}
45 1162 : , fEnd {block + SkTo<uint32_t>(size)}
46 : , fFirstBlock {block}
47 1162 : , fFirstSize {SkTo<uint32_t>(size)}
48 3486 : , fExtraSize {SkTo<uint32_t>(extraSize)}
49 : {
50 1162 : if (size < sizeof(Footer)) {
51 454 : fEnd = fCursor = fDtorCursor = nullptr;
52 : }
53 :
54 1162 : if (fCursor != nullptr) {
55 708 : this->installFooter(end_chain, 0);
56 : }
57 1162 : }
58 :
59 2320 : SkArenaAlloc::~SkArenaAlloc() {
60 1160 : RunDtorsOnBlock(fDtorCursor);
61 1160 : }
62 :
63 226 : void SkArenaAlloc::reset() {
64 226 : this->~SkArenaAlloc();
65 226 : new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize};
66 226 : }
67 :
68 1994 : void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
69 1994 : SkASSERT(padding < 64);
70 1994 : int64_t actionInt = (int64_t)(intptr_t)action;
71 :
72 : // The top 14 bits should be either all 0s or all 1s. Check this.
73 1994 : SkASSERT((actionInt << 6) >> 6 == actionInt);
74 1994 : Footer encodedFooter = (actionInt << 6) | padding;
75 1994 : memmove(fCursor, &encodedFooter, sizeof(Footer));
76 1994 : fCursor += sizeof(Footer);
77 1994 : fDtorCursor = fCursor;
78 1994 : }
79 :
80 239 : void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
81 239 : memmove(fCursor, &ptr, sizeof(char*));
82 239 : fCursor += sizeof(char*);
83 239 : this->installFooter(action, padding);
84 239 : }
85 :
86 0 : void SkArenaAlloc::installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding) {
87 0 : memmove(fCursor, &value, sizeof(uint32_t));
88 0 : fCursor += sizeof(uint32_t);
89 0 : this->installFooter(action, padding);
90 0 : }
91 :
92 239 : void SkArenaAlloc::ensureSpace(uint32_t size, uint32_t alignment) {
93 239 : constexpr uint32_t headerSize = sizeof(Footer) + sizeof(ptrdiff_t);
94 : // The chrome c++ library we use does not define std::max_align_t.
95 : // This must be conservative to add the right amount of extra memory to handle the alignment
96 : // padding.
97 239 : constexpr uint32_t alignof_max_align_t = 8;
98 239 : uint32_t objSizeAndOverhead = size + headerSize + sizeof(Footer);
99 239 : if (alignment > alignof_max_align_t) {
100 0 : objSizeAndOverhead += alignment - 1;
101 : }
102 :
103 239 : uint32_t allocationSize = std::max(objSizeAndOverhead, fExtraSize * fFib0);
104 239 : fFib0 += fFib1;
105 239 : std::swap(fFib0, fFib1);
106 :
107 : // Round up to a nice size. If > 32K align to 4K boundary else up to max_align_t. The > 32K
108 : // heuristic is from the JEMalloc behavior.
109 : {
110 239 : uint32_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 16 - 1;
111 239 : allocationSize = (allocationSize + mask) & ~mask;
112 : }
113 :
114 478 : char* newBlock = new char[allocationSize];
115 :
116 239 : auto previousDtor = fDtorCursor;
117 239 : fCursor = newBlock;
118 239 : fDtorCursor = newBlock;
119 239 : fEnd = fCursor + allocationSize;
120 239 : this->installPtrFooter(NextBlock, previousDtor, 0);
121 239 : }
122 :
123 1226 : char* SkArenaAlloc::allocObject(uint32_t size, uint32_t alignment) {
124 1226 : uintptr_t mask = alignment - 1;
125 1226 : char* objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
126 1226 : if ((ptrdiff_t)size > fEnd - objStart) {
127 239 : this->ensureSpace(size, alignment);
128 239 : objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
129 : }
130 1226 : return objStart;
131 : }
132 :
133 1047 : char* SkArenaAlloc::allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment) {
134 1047 : uintptr_t mask = alignment - 1;
135 :
136 : restart:
137 1047 : uint32_t skipOverhead = 0;
138 1047 : bool needsSkipFooter = fCursor != fDtorCursor;
139 1047 : if (needsSkipFooter) {
140 0 : skipOverhead = sizeof(Footer) + sizeof(uint32_t);
141 : }
142 1047 : char* objStart = (char*)((uintptr_t)(fCursor + skipOverhead + mask) & ~mask);
143 1047 : uint32_t totalSize = sizeIncludingFooter + skipOverhead;
144 :
145 1047 : if ((ptrdiff_t)totalSize > fEnd - objStart) {
146 0 : this->ensureSpace(totalSize, alignment);
147 0 : goto restart;
148 : }
149 :
150 1047 : SkASSERT((ptrdiff_t)totalSize <= fEnd - objStart);
151 :
152 : // Install a skip footer if needed, thus terminating a run of POD data. The calling code is
153 : // responsible for installing the footer after the object.
154 1047 : if (needsSkipFooter) {
155 0 : this->installUint32Footer(SkipPod, SkTo<uint32_t>(fCursor - fDtorCursor), 0);
156 : }
157 :
158 1047 : return objStart;
159 : }
160 :
|