blob: d6c249b57390936a259bedfcd663cda245d7a8f8 [file] [log] [blame]
Mike Klein58b13062016-11-11 10:38:49 -05001/*
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
Herb Derby0497f082017-01-13 11:30:44 -05008#include <algorithm>
Herb Derbyac04fef2017-01-13 17:34:33 -05009#include "SkArenaAlloc.h"
Herb Derby0497f082017-01-13 11:30:44 -050010
11struct Skipper {
12 char* operator()(char* objEnd, ptrdiff_t size) { return objEnd + size; }
13};
14
Herb Derby15172242017-01-19 03:32:23 +000015struct NextBlock {
16 char* operator()(char* objEnd, ptrdiff_t size) { delete [] objEnd; return objEnd + size; }
Herb Derby0497f082017-01-13 11:30:44 -050017};
18
19SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize)
Herb Derby15172242017-01-19 03:32:23 +000020 : fDtorCursor{block}
21 , fCursor {block}
22 , fEnd {block + size}
23 , fExtraSize {extraSize}
Herb Derby0497f082017-01-13 11:30:44 -050024{
25 if (size < sizeof(Footer)) {
26 fEnd = fCursor = fDtorCursor = nullptr;
27 }
28
29 if (fCursor != nullptr) {
30 this->installFooter(EndChain, 0);
31 }
32}
33
34SkArenaAlloc::~SkArenaAlloc() {
Herb Derby15172242017-01-19 03:32:23 +000035 this->reset();
Herb Derby0497f082017-01-13 11:30:44 -050036}
37
38void SkArenaAlloc::reset() {
Herb Derby15172242017-01-19 03:32:23 +000039 Footer f;
40 memmove(&f, fDtorCursor - sizeof(Footer), sizeof(Footer));
41 char* releaser = fDtorCursor;
42 while (releaser != nullptr) {
43 releaser = this->callFooterAction(releaser);
44 }
Herb Derby0497f082017-01-13 11:30:44 -050045}
46
47void SkArenaAlloc::installFooter(FooterAction* releaser, ptrdiff_t padding) {
48 ptrdiff_t releaserDiff = (char *)releaser - (char *)EndChain;
49 ptrdiff_t footerData = SkLeftShift((int64_t)releaserDiff, 5) | padding;
50 if (padding >= 32 || !SkTFitsIn<int32_t>(footerData)) {
51 // Footer data will not fit.
52 SkFAIL("Constraints are busted.");
53 }
54
55 Footer footer = (Footer)(footerData);
56 memmove(fCursor, &footer, sizeof(Footer));
Herb Derby15172242017-01-19 03:32:23 +000057 Footer check;
58 memmove(&check, fCursor, sizeof(Footer));
Herb Derby0497f082017-01-13 11:30:44 -050059 fCursor += sizeof(Footer);
60 fDtorCursor = fCursor;
61}
62
63void SkArenaAlloc::ensureSpace(size_t size, size_t alignment) {
64 constexpr size_t headerSize = sizeof(Footer) + sizeof(ptrdiff_t);
65 // The chrome c++ library we use does not define std::max_align_t.
66 // This must be conservative to add the right amount of extra memory to handle the alignment
67 // padding.
68 constexpr size_t alignof_max_align_t = 8;
69 auto objSizeAndOverhead = size + headerSize + sizeof(Footer);
70 if (alignment > alignof_max_align_t) {
71 objSizeAndOverhead += alignment - 1;
72 }
73
74 auto allocationSize = std::max(objSizeAndOverhead, fExtraSize);
75
76 // Round up to a nice size. If > 32K align to 4K boundary else up to max_align_t. The > 32K
77 // heuristic is from the JEMalloc behavior.
78 {
79 size_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 32 - 1;
80 allocationSize = (allocationSize + mask) & ~mask;
81 }
82
83 char* newBlock = new char[allocationSize];
84
85 auto previousDtor = fDtorCursor;
86 fCursor = newBlock;
87 fDtorCursor = newBlock;
88 fEnd = fCursor + allocationSize;
89 this->installIntFooter<NextBlock>(previousDtor - fCursor, 0);
90}
91
92char* SkArenaAlloc::allocObject(size_t size, size_t alignment) {
93 size_t mask = alignment - 1;
94 char* objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
95 if (objStart + size > fEnd) {
96 this->ensureSpace(size, alignment);
97 objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
98 }
99 return objStart;
100}
101
102// * sizeAndFooter - the memory for the footer in addition to the size for the object.
103// * alignment - alignment needed by the object.
104char* SkArenaAlloc::allocObjectWithFooter(size_t sizeIncludingFooter, size_t alignment) {
105 size_t mask = alignment - 1;
106
Herb Derby15172242017-01-19 03:32:23 +0000107 restart:
Herb Derby0497f082017-01-13 11:30:44 -0500108 size_t skipOverhead = 0;
109 bool needsSkipFooter = fCursor != fDtorCursor;
110 if (needsSkipFooter) {
111 size_t skipSize = SkTFitsIn<int32_t>(fDtorCursor - fCursor)
112 ? sizeof(int32_t)
113 : sizeof(ptrdiff_t);
114 skipOverhead = sizeof(Footer) + skipSize;
115 }
116 char* objStart = (char*)((uintptr_t)(fCursor + skipOverhead + mask) & ~mask);
117 size_t totalSize = sizeIncludingFooter + skipOverhead;
118
119 if (objStart + totalSize > fEnd) {
120 this->ensureSpace(totalSize, alignment);
121 goto restart;
122 }
123
124 SkASSERT(objStart + totalSize <= fEnd);
125
126 // Install a skip footer if needed, thus terminating a run of POD data. The calling code is
127 // responsible for installing the footer after the object.
128 if (needsSkipFooter) {
129 this->installIntFooter<Skipper>(fDtorCursor - fCursor, 0);
130 }
131
132 return objStart;
133}
134
Herb Derby15172242017-01-19 03:32:23 +0000135char* SkArenaAlloc::callFooterAction(char* end) {
Herb Derby412a86d2017-01-18 17:07:48 -0500136 Footer footer;
Herb Derby15172242017-01-19 03:32:23 +0000137 memcpy(&footer, end - sizeof(Footer), sizeof(Footer));
Herb Derby412a86d2017-01-18 17:07:48 -0500138
Herb Derby15172242017-01-19 03:32:23 +0000139 FooterAction* releaser = (FooterAction*)((char*)EndChain + (footer >> 5));
Herb Derby0497f082017-01-13 11:30:44 -0500140 ptrdiff_t padding = footer & 31;
141
Herb Derby15172242017-01-19 03:32:23 +0000142 char* r = releaser(end) - padding;
Herb Derby0497f082017-01-13 11:30:44 -0500143
144 return r;
145}
146
147char* SkArenaAlloc::EndChain(char*) { return nullptr; }
148