blob: e5d2f4ea610961e385f9b72c4f469e6ae1a1e547 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2010 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.
reed@google.comac10a2d2010-12-22 21:39:39 +00006 */
7
reed@google.comac10a2d2010-12-22 21:39:39 +00008#ifndef GrAllocator_DEFINED
9#define GrAllocator_DEFINED
10
11#include "GrConfig.h"
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +000012#include "GrTypes.h"
bsalomon@google.com49313f62011-09-14 13:54:05 +000013#include "SkTArray.h"
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +000014#include "SkTypes.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000015
commit-bot@chromium.orge3beb6b2014-04-07 19:34:38 +000016class GrAllocator : SkNoncopyable {
reed@google.comac10a2d2010-12-22 21:39:39 +000017public:
bsalomonbce3d6d2014-07-02 07:54:42 -070018 ~GrAllocator() { this->reset(); }
reed@google.comac10a2d2010-12-22 21:39:39 +000019
20 /**
21 * Create an allocator
22 *
23 * @param itemSize the size of each item to allocate
24 * @param itemsPerBlock the number of items to allocate at once
25 * @param initialBlock optional memory to use for the first block.
26 * Must be at least itemSize*itemsPerBlock sized.
27 * Caller is responsible for freeing this memory.
28 */
bsalomonbce3d6d2014-07-02 07:54:42 -070029 GrAllocator(size_t itemSize, int itemsPerBlock, void* initialBlock)
30 : fItemSize(itemSize)
31 , fItemsPerBlock(itemsPerBlock)
halcanary96fcdcc2015-08-27 07:41:13 -070032 , fOwnFirstBlock(nullptr == initialBlock)
bsalomonbce3d6d2014-07-02 07:54:42 -070033 , fCount(0)
34 , fInsertionIndexInBlock(0) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000035 SkASSERT(itemsPerBlock > 0);
reed@google.comac10a2d2010-12-22 21:39:39 +000036 fBlockSize = fItemSize * fItemsPerBlock;
bsalomonbce3d6d2014-07-02 07:54:42 -070037 if (fOwnFirstBlock) {
38 // This force us to allocate a new block on push_back().
39 fInsertionIndexInBlock = fItemsPerBlock;
40 } else {
41 fBlocks.push_back() = initialBlock;
42 fInsertionIndexInBlock = 0;
43 }
reed@google.comac10a2d2010-12-22 21:39:39 +000044 }
45
bsalomonbce3d6d2014-07-02 07:54:42 -070046 /**
47 * Adds an item and returns pointer to it.
48 *
49 * @return pointer to the added item.
50 */
51 void* push_back() {
52 // we always have at least one block
53 if (fItemsPerBlock == fInsertionIndexInBlock) {
54 fBlocks.push_back() = sk_malloc_throw(fBlockSize);
55 fInsertionIndexInBlock = 0;
56 }
57 void* ret = (char*)fBlocks.back() + fItemSize * fInsertionIndexInBlock;
58 ++fCount;
59 ++fInsertionIndexInBlock;
60 return ret;
61 }
62
63 /**
bsalomona1ae66d2014-09-05 06:13:43 -070064 * Remove the last item, only call if count() != 0
65 */
66 void pop_back() {
67 SkASSERT(fCount);
68 SkASSERT(fInsertionIndexInBlock > 0);
69 --fInsertionIndexInBlock;
70 --fCount;
71 if (0 == fInsertionIndexInBlock) {
72 // Never delete the first block
73 if (fBlocks.count() > 1) {
74 sk_free(fBlocks.back());
75 fBlocks.pop_back();
76 fInsertionIndexInBlock = fItemsPerBlock;
77 }
78 }
79 }
80
81 /**
bsalomonbce3d6d2014-07-02 07:54:42 -070082 * Removes all added items.
83 */
84 void reset() {
85 int firstBlockToFree = fOwnFirstBlock ? 0 : 1;
86 for (int i = firstBlockToFree; i < fBlocks.count(); ++i) {
87 sk_free(fBlocks[i]);
88 }
89 if (fOwnFirstBlock) {
90 fBlocks.reset();
91 // This force us to allocate a new block on push_back().
92 fInsertionIndexInBlock = fItemsPerBlock;
93 } else {
94 fBlocks.pop_back_n(fBlocks.count() - 1);
95 fInsertionIndexInBlock = 0;
96 }
97 fCount = 0;
98 }
99
100 /**
101 * Returns the item count.
102 */
103 int count() const {
104 return fCount;
105 }
106
107 /**
108 * Is the count 0?
109 */
110 bool empty() const { return 0 == fCount; }
111
112 /**
113 * Access last item, only call if count() != 0
114 */
115 void* back() {
116 SkASSERT(fCount);
117 SkASSERT(fInsertionIndexInBlock > 0);
118 return (char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
119 }
120
121 /**
122 * Access last item, only call if count() != 0
123 */
124 const void* back() const {
125 SkASSERT(fCount);
126 SkASSERT(fInsertionIndexInBlock > 0);
127 return (const char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
128 }
129
bsalomonbce3d6d2014-07-02 07:54:42 -0700130 /**
131 * Iterates through the allocator. This is faster than using operator[] when walking linearly
132 * through the allocator.
133 */
134 class Iter {
135 public:
136 /**
137 * Initializes the iterator. next() must be called before get().
138 */
139 Iter(const GrAllocator* allocator)
140 : fAllocator(allocator)
141 , fBlockIndex(-1)
142 , fIndexInBlock(allocator->fItemsPerBlock - 1)
143 , fItemIndex(-1) {}
144
145 /**
146 * Advances the iterator. Iteration is finished when next() returns false.
147 */
148 bool next() {
149 ++fIndexInBlock;
150 ++fItemIndex;
151 if (fIndexInBlock == fAllocator->fItemsPerBlock) {
152 ++fBlockIndex;
153 fIndexInBlock = 0;
154 }
155 return fItemIndex < fAllocator->fCount;
156 }
157
158 /**
159 * Gets the current iterator value. Call next() at least once before calling. Don't call
160 * after next() returns false.
161 */
bsalomone1bc1c62014-07-02 13:44:57 -0700162 void* get() const {
bsalomonbce3d6d2014-07-02 07:54:42 -0700163 SkASSERT(fItemIndex >= 0 && fItemIndex < fAllocator->fCount);
164 return (char*) fAllocator->fBlocks[fBlockIndex] + fIndexInBlock * fAllocator->fItemSize;
165 }
166
167 private:
168 const GrAllocator* fAllocator;
169 int fBlockIndex;
170 int fIndexInBlock;
171 int fItemIndex;
172 };
173
174 /**
175 * Access item by index.
176 */
177 void* operator[] (int i) {
178 SkASSERT(i >= 0 && i < fCount);
179 return (char*)fBlocks[i / fItemsPerBlock] +
180 fItemSize * (i % fItemsPerBlock);
181 }
182
183 /**
184 * Access item by index.
185 */
186 const void* operator[] (int i) const {
187 SkASSERT(i >= 0 && i < fCount);
188 return (const char*)fBlocks[i / fItemsPerBlock] +
189 fItemSize * (i % fItemsPerBlock);
190 }
191
192protected:
193 /**
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000194 * Set first block of memory to write into. Must be called before any other methods.
halcanary96fcdcc2015-08-27 07:41:13 -0700195 * This requires that you have passed nullptr in the constructor.
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000196 *
197 * @param initialBlock optional memory to use for the first block.
198 * Must be at least itemSize*itemsPerBlock sized.
199 * Caller is responsible for freeing this memory.
200 */
201 void setInitialBlock(void* initialBlock) {
202 SkASSERT(0 == fCount);
bsalomonbce3d6d2014-07-02 07:54:42 -0700203 SkASSERT(0 == fBlocks.count());
204 SkASSERT(fItemsPerBlock == fInsertionIndexInBlock);
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000205 fOwnFirstBlock = false;
bsalomonbce3d6d2014-07-02 07:54:42 -0700206 fBlocks.push_back() = initialBlock;
207 fInsertionIndexInBlock = 0;
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000208 }
209
bsalomonbce3d6d2014-07-02 07:54:42 -0700210 // For access to above function.
211 template <typename T> friend class GrTAllocator;
reed@google.comac10a2d2010-12-22 21:39:39 +0000212
213private:
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000214 static const int NUM_INIT_BLOCK_PTRS = 8;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000215
bsalomonbce3d6d2014-07-02 07:54:42 -0700216 SkSTArray<NUM_INIT_BLOCK_PTRS, void*, true> fBlocks;
217 size_t fBlockSize;
218 size_t fItemSize;
219 int fItemsPerBlock;
220 bool fOwnFirstBlock;
221 int fCount;
222 int fInsertionIndexInBlock;
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000223
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +0000224 typedef SkNoncopyable INHERITED;
reed@google.comac10a2d2010-12-22 21:39:39 +0000225};
226
bsalomonb3e3a952014-09-19 11:10:40 -0700227template <typename T> class GrTAllocator;
228template <typename T> void* operator new(size_t, GrTAllocator<T>*);
229
230template <typename T> class GrTAllocator : SkNoncopyable {
reed@google.comac10a2d2010-12-22 21:39:39 +0000231public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400232 virtual ~GrTAllocator() { this->reset(); }
bsalomon@google.coma55847b2011-04-20 15:47:04 +0000233
reed@google.comac10a2d2010-12-22 21:39:39 +0000234 /**
235 * Create an allocator
236 *
237 * @param itemsPerBlock the number of items to allocate at once
reed@google.comac10a2d2010-12-22 21:39:39 +0000238 */
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000239 explicit GrTAllocator(int itemsPerBlock)
halcanary96fcdcc2015-08-27 07:41:13 -0700240 : fAllocator(sizeof(T), itemsPerBlock, nullptr) {}
bsalomon@google.coma55847b2011-04-20 15:47:04 +0000241
242 /**
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 * Adds an item and returns it.
244 *
245 * @return the added item.
246 */
247 T& push_back() {
248 void* item = fAllocator.push_back();
bsalomon49f085d2014-09-05 13:34:00 -0700249 SkASSERT(item);
halcanary385fe4d2015-08-26 13:07:48 -0700250 new (item) T;
reed@google.comac10a2d2010-12-22 21:39:39 +0000251 return *(T*)item;
252 }
bsalomon@google.com4fa66942011-09-20 19:06:12 +0000253
254 T& push_back(const T& t) {
255 void* item = fAllocator.push_back();
bsalomon49f085d2014-09-05 13:34:00 -0700256 SkASSERT(item);
halcanary385fe4d2015-08-26 13:07:48 -0700257 new (item) T(t);
bsalomon@google.com4fa66942011-09-20 19:06:12 +0000258 return *(T*)item;
259 }
260
Brian Salomonbc6b99d2017-01-11 10:32:34 -0500261 template <typename... Args> T& emplace_back(Args&&... args) {
262 void* item = fAllocator.push_back();
263 SkASSERT(item);
264 new (item) T(std::forward<Args>(args)...);
265 return *(T*)item;
266 }
267
reed@google.comac10a2d2010-12-22 21:39:39 +0000268 /**
bsalomona1ae66d2014-09-05 06:13:43 -0700269 * Remove the last item, only call if count() != 0
270 */
271 void pop_back() {
272 this->back().~T();
273 fAllocator.pop_back();
274 }
275
276 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700277 * Removes all added items.
reed@google.comac10a2d2010-12-22 21:39:39 +0000278 */
279 void reset() {
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000280 int c = fAllocator.count();
281 for (int i = 0; i < c; ++i) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000282 ((T*)fAllocator[i])->~T();
283 }
284 fAllocator.reset();
285 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000286
reed@google.comac10a2d2010-12-22 21:39:39 +0000287 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700288 * Returns the item count.
reed@google.comac10a2d2010-12-22 21:39:39 +0000289 */
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000290 int count() const {
reed@google.comac10a2d2010-12-22 21:39:39 +0000291 return fAllocator.count();
292 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000293
reed@google.comac10a2d2010-12-22 21:39:39 +0000294 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700295 * Is the count 0?
reed@google.comac10a2d2010-12-22 21:39:39 +0000296 */
297 bool empty() const { return fAllocator.empty(); }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000298
reed@google.comac10a2d2010-12-22 21:39:39 +0000299 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700300 * Access last item, only call if count() != 0
reed@google.comac10a2d2010-12-22 21:39:39 +0000301 */
302 T& back() {
303 return *(T*)fAllocator.back();
304 }
305
306 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700307 * Access last item, only call if count() != 0
reed@google.comac10a2d2010-12-22 21:39:39 +0000308 */
309 const T& back() const {
310 return *(const T*)fAllocator.back();
311 }
312
313 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700314 * Iterates through the allocator. This is faster than using operator[] when walking linearly
315 * through the allocator.
316 */
317 class Iter {
318 public:
319 /**
320 * Initializes the iterator. next() must be called before get() or ops * and ->.
321 */
322 Iter(const GrTAllocator* allocator) : fImpl(&allocator->fAllocator) {}
323
324 /**
325 * Advances the iterator. Iteration is finished when next() returns false.
326 */
327 bool next() { return fImpl.next(); }
328
329 /**
330 * Gets the current iterator value. Call next() at least once before calling. Don't call
331 * after next() returns false.
332 */
bsalomone1bc1c62014-07-02 13:44:57 -0700333 T* get() const { return (T*) fImpl.get(); }
bsalomonbce3d6d2014-07-02 07:54:42 -0700334
335 /**
336 * Convenience operators. Same rules for calling apply as get().
337 */
bsalomone1bc1c62014-07-02 13:44:57 -0700338 T& operator*() const { return *this->get(); }
339 T* operator->() const { return this->get(); }
bsalomonbce3d6d2014-07-02 07:54:42 -0700340
341 private:
342 GrAllocator::Iter fImpl;
343 };
344
345 /**
346 * Access item by index.
rmistry@google.comd6176b02012-08-23 18:14:13 +0000347 */
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000348 T& operator[] (int i) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000349 return *(T*)(fAllocator[i]);
350 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000351
reed@google.comac10a2d2010-12-22 21:39:39 +0000352 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700353 * Access item by index.
reed@google.comac10a2d2010-12-22 21:39:39 +0000354 */
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000355 const T& operator[] (int i) const {
reed@google.comac10a2d2010-12-22 21:39:39 +0000356 return *(const T*)(fAllocator[i]);
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000357 }
358
359protected:
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000360 /*
361 * Set first block of memory to write into. Must be called before any other methods.
362 *
363 * @param initialBlock optional memory to use for the first block.
364 * Must be at least size(T)*itemsPerBlock sized.
365 * Caller is responsible for freeing this memory.
366 */
367 void setInitialBlock(void* initialBlock) {
368 fAllocator.setInitialBlock(initialBlock);
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000369 }
370
371private:
bsalomonb3e3a952014-09-19 11:10:40 -0700372 friend void* operator new<T>(size_t, GrTAllocator*);
373
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000374 GrAllocator fAllocator;
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +0000375 typedef SkNoncopyable INHERITED;
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000376};
377
378template <int N, typename T> class GrSTAllocator : public GrTAllocator<T> {
379private:
380 typedef GrTAllocator<T> INHERITED;
381
382public:
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000383 GrSTAllocator() : INHERITED(N) {
384 this->setInitialBlock(fStorage.get());
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000385 }
386
387private:
388 SkAlignedSTStorage<N, T> fStorage;
reed@google.comac10a2d2010-12-22 21:39:39 +0000389};
390
bsalomonb3e3a952014-09-19 11:10:40 -0700391template <typename T> void* operator new(size_t size, GrTAllocator<T>* allocator) {
392 return allocator->fAllocator.push_back();
393}
394
395// Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete
396// to match the op new silences warnings about missing op delete when a constructor throws an
397// exception.
398template <typename T> void operator delete(void*, GrTAllocator<T>*) {
djsollenf2b340f2016-01-29 08:51:04 -0800399 SK_ABORT("Invalid Operation");
bsalomonb3e3a952014-09-19 11:10:40 -0700400}
401
402#define GrNEW_APPEND_TO_ALLOCATOR(allocator_ptr, type_name, args) \
403 new (allocator_ptr) type_name args
404
reed@google.comac10a2d2010-12-22 21:39:39 +0000405#endif