blob: 8152c94cbd1676333b25f2c02109757b6bb6ead8 [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
8#ifndef SkFixedAlloc_DEFINED
9#define SkFixedAlloc_DEFINED
10
11#include "SkTFitsIn.h"
12#include "SkTypes.h"
Herb Derby0497f082017-01-13 11:30:44 -050013#include <cstddef>
Mike Klein58b13062016-11-11 10:38:49 -050014#include <new>
Herb Derby0497f082017-01-13 11:30:44 -050015#include <type_traits>
Mike Klein58b13062016-11-11 10:38:49 -050016#include <utility>
17#include <vector>
18
Herb Derby0497f082017-01-13 11:30:44 -050019// SkArenaAlloc allocates object and destroys the allocated objects when destroyed. It's designed
20// to minimize the number of underlying block allocations. SkArenaAlloc allocates first out of an
21// (optional) user-provided block of memory, and when that's exhausted it allocates on the heap,
22// starting with an allocation of extraSize bytes. If your data (plus a small overhead) fits in
23// the user-provided block, SkArenaAlloc never uses the heap, and if it fits in extraSize bytes,
24// it'll use the heap only once. If you pass extraSize = 0, it allocates blocks for each call to
25// make<T>.
26//
27// Examples:
28//
29// char block[mostCasesSize];
30// SkArenaAlloc arena(block, almostAllCasesSize);
31//
32// If mostCasesSize is too large for the stack, you can use the following pattern.
33//
34// std::unique_ptr<char[]> block{new char[mostCasesSize]};
35// SkArenaAlloc arena(block.get(), mostCasesSize, almostAllCasesSize);
36//
37// If the program only sometimes allocates memory, use the following.
38//
39// SkArenaAlloc arena(nullptr, 0, almostAllCasesSize);
40//
41// The storage does not necessarily need to be on the stack. Embedding the storage in a class also
42// works.
43//
44// class Foo {
45// char storage[mostCasesSize];
46// SkArenaAlloc arena (storage, almostAllCasesSize);
47// };
48//
49// In addition, the system is optimized to handle POD data including arrays of PODs (where
50// POD is really data with no destructors). For POD data it has zero overhead per item, and a
51// typical block overhead of 8 bytes. For non-POD objects there is a per item overhead of 4 bytes.
52// For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There is an
53// addition overhead when switching from POD data to non-POD data of typically 8 bytes.
54class SkArenaAlloc {
55public:
56 SkArenaAlloc(char* block, size_t size, size_t extraSize = 0);
57
58 template <size_t kSize>
Herb Derby15172242017-01-19 03:32:23 +000059 SkArenaAlloc(char (&block)[kSize], size_t extraSize = 0)
Herb Derby0497f082017-01-13 11:30:44 -050060 : SkArenaAlloc(block, kSize, extraSize)
61 {}
62
63 ~SkArenaAlloc();
64
65 template <typename T, typename... Args>
66 T* make(Args&&... args) {
67 char* objStart;
68 if (skstd::is_trivially_destructible<T>::value) {
69 objStart = this->allocObject(sizeof(T), alignof(T));
70 fCursor = objStart + sizeof(T);
71 } else {
72 objStart = this->allocObjectWithFooter(sizeof(T) + sizeof(Footer), alignof(T));
73 size_t padding = objStart - fCursor;
74
75 // Advance to end of object to install footer.
76 fCursor = objStart + sizeof(T);
77 FooterAction* releaser = [](char* objEnd) {
78 char* objStart = objEnd - (sizeof(T) + sizeof(Footer));
79 ((T*)objStart)->~T();
80 return objStart;
81 };
82 this->installFooter(releaser, padding);
83 }
84
85 // This must be last to make objects with nested use of this allocator work.
86 return new(objStart) T(std::forward<Args>(args)...);
87 }
88
89 template <typename T>
90 T* makeArrayDefault(size_t count) {
91 T* array = (T*)this->commonArrayAlloc<T>(count);
92
93 // If T is primitive then no initialization takes place.
94 for (size_t i = 0; i < count; i++) {
95 new (&array[i]) T;
96 }
97 return array;
98 }
99
100 template <typename T>
101 T* makeArray(size_t count) {
102 T* array = (T*)this->commonArrayAlloc<T>(count);
103
104 // If T is primitive then the memory is initialized. For example, an array of chars will
105 // be zeroed.
106 for (size_t i = 0; i < count; i++) {
107 new (&array[i]) T();
108 }
109 return array;
110 }
111
112 // Destroy all allocated objects, free any heap allocations.
113 void reset();
114
115private:
116 using Footer = int32_t;
117 using FooterAction = char* (char*);
118
119 void installFooter(FooterAction* releaser, ptrdiff_t padding);
120
121 // N.B. Action is different than FooterAction. FooterAction expects the end of the Footer,
122 // and returns the start of the object. An Action expects the end of the *Object* and returns
123 // the start of the object.
124 template<typename Action>
125 void installIntFooter(ptrdiff_t size, ptrdiff_t padding) {
126 if (SkTFitsIn<int32_t>(size)) {
127 int32_t smallSize = static_cast<int32_t>(size);
128 memmove(fCursor, &smallSize, sizeof(int32_t));
129 fCursor += sizeof(int32_t);
130 this->installFooter(
131 [](char* footerEnd) {
132 char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
133 int32_t data;
134 memmove(&data, objEnd, sizeof(int32_t));
135 return Action()(objEnd, data);
136 },
137 padding);
138 } else {
139 memmove(fCursor, &size, sizeof(ptrdiff_t));
140 fCursor += sizeof(ptrdiff_t);
141 this->installFooter(
142 [](char* footerEnd) {
143 char* objEnd = footerEnd - (sizeof(Footer) + sizeof(ptrdiff_t));
144 ptrdiff_t data;
145 memmove(&data, objEnd, sizeof(ptrdiff_t));
146 return Action()(objEnd, data);
147 },
148 padding);
149 }
150 }
151
152 void ensureSpace(size_t size, size_t alignment);
153
154 char* allocObject(size_t size, size_t alignment);
155
156 char* allocObjectWithFooter(size_t sizeIncludingFooter, size_t alignment);
157
158 template <typename T>
159 char* commonArrayAlloc(size_t count) {
160 char* objStart;
161 size_t arraySize = count * sizeof(T);
162
163 SkASSERT(arraySize > 0);
164
165 if (skstd::is_trivially_destructible<T>::value) {
166 objStart = this->allocObject(arraySize, alignof(T));
167 fCursor = objStart + arraySize;
168 } else {
169 size_t countSize = SkTFitsIn<int32_t>(count) ? sizeof(int32_t) : sizeof(ptrdiff_t);
170 size_t totalSize = arraySize + sizeof(Footer) + countSize;
171 objStart = this->allocObjectWithFooter(totalSize, alignof(T));
172 size_t padding = objStart - fCursor;
173
174 // Advance to end of array to install footer.?
175 fCursor = objStart + arraySize;
176 this->installIntFooter<ArrayDestructor<T>> (count, padding);
177 }
178
179 return objStart;
180 }
181
Herb Derby15172242017-01-19 03:32:23 +0000182 char* callFooterAction(char* end);
Herb Derby0497f082017-01-13 11:30:44 -0500183
184 static char* EndChain(char*);
185
186 template<typename T>
187 struct ArrayDestructor {
188 char* operator()(char* objEnd, ptrdiff_t count) {
189 char* objStart = objEnd - count * sizeof(T);
190 T* array = (T*) objStart;
191 for (int i = 0; i < count; i++) {
192 array[i].~T();
193 }
194 return objStart;
195 }
196 };
197
Herb Derby15172242017-01-19 03:32:23 +0000198 char* fDtorCursor;
199 char* fCursor;
200 char* fEnd;
201 size_t fExtraSize;
Herb Derby0497f082017-01-13 11:30:44 -0500202};
203
Mike Klein58b13062016-11-11 10:38:49 -0500204#endif//SkFixedAlloc_DEFINED