blob: b879b846d27baca2d40ecb292158d4302d058e68 [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"
Mike Klein79aea6a2018-06-11 10:45:26 -040015#include <new>
reed@google.comac10a2d2010-12-22 21:39:39 +000016
commit-bot@chromium.orge3beb6b2014-04-07 19:34:38 +000017class GrAllocator : SkNoncopyable {
reed@google.comac10a2d2010-12-22 21:39:39 +000018public:
bsalomonbce3d6d2014-07-02 07:54:42 -070019 ~GrAllocator() { this->reset(); }
reed@google.comac10a2d2010-12-22 21:39:39 +000020
21 /**
22 * Create an allocator
23 *
24 * @param itemSize the size of each item to allocate
25 * @param itemsPerBlock the number of items to allocate at once
26 * @param initialBlock optional memory to use for the first block.
27 * Must be at least itemSize*itemsPerBlock sized.
28 * Caller is responsible for freeing this memory.
29 */
bsalomonbce3d6d2014-07-02 07:54:42 -070030 GrAllocator(size_t itemSize, int itemsPerBlock, void* initialBlock)
31 : fItemSize(itemSize)
32 , fItemsPerBlock(itemsPerBlock)
halcanary96fcdcc2015-08-27 07:41:13 -070033 , fOwnFirstBlock(nullptr == initialBlock)
bsalomonbce3d6d2014-07-02 07:54:42 -070034 , fCount(0)
35 , fInsertionIndexInBlock(0) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000036 SkASSERT(itemsPerBlock > 0);
reed@google.comac10a2d2010-12-22 21:39:39 +000037 fBlockSize = fItemSize * fItemsPerBlock;
bsalomonbce3d6d2014-07-02 07:54:42 -070038 if (fOwnFirstBlock) {
39 // This force us to allocate a new block on push_back().
40 fInsertionIndexInBlock = fItemsPerBlock;
41 } else {
42 fBlocks.push_back() = initialBlock;
43 fInsertionIndexInBlock = 0;
44 }
reed@google.comac10a2d2010-12-22 21:39:39 +000045 }
46
bsalomonbce3d6d2014-07-02 07:54:42 -070047 /**
48 * Adds an item and returns pointer to it.
49 *
50 * @return pointer to the added item.
51 */
52 void* push_back() {
53 // we always have at least one block
54 if (fItemsPerBlock == fInsertionIndexInBlock) {
55 fBlocks.push_back() = sk_malloc_throw(fBlockSize);
56 fInsertionIndexInBlock = 0;
57 }
58 void* ret = (char*)fBlocks.back() + fItemSize * fInsertionIndexInBlock;
59 ++fCount;
60 ++fInsertionIndexInBlock;
61 return ret;
62 }
63
64 /**
bsalomona1ae66d2014-09-05 06:13:43 -070065 * Remove the last item, only call if count() != 0
66 */
67 void pop_back() {
68 SkASSERT(fCount);
69 SkASSERT(fInsertionIndexInBlock > 0);
70 --fInsertionIndexInBlock;
71 --fCount;
72 if (0 == fInsertionIndexInBlock) {
73 // Never delete the first block
74 if (fBlocks.count() > 1) {
75 sk_free(fBlocks.back());
76 fBlocks.pop_back();
77 fInsertionIndexInBlock = fItemsPerBlock;
78 }
79 }
80 }
81
82 /**
bsalomonbce3d6d2014-07-02 07:54:42 -070083 * Removes all added items.
84 */
85 void reset() {
86 int firstBlockToFree = fOwnFirstBlock ? 0 : 1;
87 for (int i = firstBlockToFree; i < fBlocks.count(); ++i) {
88 sk_free(fBlocks[i]);
89 }
90 if (fOwnFirstBlock) {
91 fBlocks.reset();
92 // This force us to allocate a new block on push_back().
93 fInsertionIndexInBlock = fItemsPerBlock;
94 } else {
95 fBlocks.pop_back_n(fBlocks.count() - 1);
96 fInsertionIndexInBlock = 0;
97 }
98 fCount = 0;
99 }
100
101 /**
102 * Returns the item count.
103 */
104 int count() const {
105 return fCount;
106 }
107
108 /**
109 * Is the count 0?
110 */
111 bool empty() const { return 0 == fCount; }
112
113 /**
Chris Daltond0d409d2018-06-08 11:51:39 -0600114 * Access first item, only call if count() != 0
115 */
116 void* front() {
117 SkASSERT(fCount);
118 SkASSERT(fInsertionIndexInBlock > 0);
119 return (char*)(fBlocks.front());
120 }
121
122 /**
123 * Access first item, only call if count() != 0
124 */
125 const void* front() const {
126 SkASSERT(fCount);
127 SkASSERT(fInsertionIndexInBlock > 0);
128 return (const char*)(fBlocks.front());
129 }
130
131 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700132 * Access last item, only call if count() != 0
133 */
134 void* back() {
135 SkASSERT(fCount);
136 SkASSERT(fInsertionIndexInBlock > 0);
137 return (char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
138 }
139
140 /**
141 * Access last item, only call if count() != 0
142 */
143 const void* back() const {
144 SkASSERT(fCount);
145 SkASSERT(fInsertionIndexInBlock > 0);
146 return (const char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
147 }
148
bsalomonbce3d6d2014-07-02 07:54:42 -0700149 /**
150 * Iterates through the allocator. This is faster than using operator[] when walking linearly
151 * through the allocator.
152 */
153 class Iter {
154 public:
155 /**
156 * Initializes the iterator. next() must be called before get().
157 */
158 Iter(const GrAllocator* allocator)
159 : fAllocator(allocator)
160 , fBlockIndex(-1)
161 , fIndexInBlock(allocator->fItemsPerBlock - 1)
162 , fItemIndex(-1) {}
163
164 /**
165 * Advances the iterator. Iteration is finished when next() returns false.
166 */
167 bool next() {
168 ++fIndexInBlock;
169 ++fItemIndex;
170 if (fIndexInBlock == fAllocator->fItemsPerBlock) {
171 ++fBlockIndex;
172 fIndexInBlock = 0;
173 }
174 return fItemIndex < fAllocator->fCount;
175 }
176
177 /**
178 * Gets the current iterator value. Call next() at least once before calling. Don't call
179 * after next() returns false.
180 */
bsalomone1bc1c62014-07-02 13:44:57 -0700181 void* get() const {
bsalomonbce3d6d2014-07-02 07:54:42 -0700182 SkASSERT(fItemIndex >= 0 && fItemIndex < fAllocator->fCount);
183 return (char*) fAllocator->fBlocks[fBlockIndex] + fIndexInBlock * fAllocator->fItemSize;
184 }
185
186 private:
187 const GrAllocator* fAllocator;
188 int fBlockIndex;
189 int fIndexInBlock;
190 int fItemIndex;
191 };
192
193 /**
194 * Access item by index.
195 */
196 void* operator[] (int i) {
197 SkASSERT(i >= 0 && i < fCount);
198 return (char*)fBlocks[i / fItemsPerBlock] +
199 fItemSize * (i % fItemsPerBlock);
200 }
201
202 /**
203 * Access item by index.
204 */
205 const void* operator[] (int i) const {
206 SkASSERT(i >= 0 && i < fCount);
207 return (const char*)fBlocks[i / fItemsPerBlock] +
208 fItemSize * (i % fItemsPerBlock);
209 }
210
211protected:
212 /**
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000213 * Set first block of memory to write into. Must be called before any other methods.
halcanary96fcdcc2015-08-27 07:41:13 -0700214 * This requires that you have passed nullptr in the constructor.
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000215 *
216 * @param initialBlock optional memory to use for the first block.
217 * Must be at least itemSize*itemsPerBlock sized.
218 * Caller is responsible for freeing this memory.
219 */
220 void setInitialBlock(void* initialBlock) {
221 SkASSERT(0 == fCount);
bsalomonbce3d6d2014-07-02 07:54:42 -0700222 SkASSERT(0 == fBlocks.count());
223 SkASSERT(fItemsPerBlock == fInsertionIndexInBlock);
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000224 fOwnFirstBlock = false;
bsalomonbce3d6d2014-07-02 07:54:42 -0700225 fBlocks.push_back() = initialBlock;
226 fInsertionIndexInBlock = 0;
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000227 }
228
bsalomonbce3d6d2014-07-02 07:54:42 -0700229 // For access to above function.
230 template <typename T> friend class GrTAllocator;
reed@google.comac10a2d2010-12-22 21:39:39 +0000231
232private:
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000233 static const int NUM_INIT_BLOCK_PTRS = 8;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000234
bsalomonbce3d6d2014-07-02 07:54:42 -0700235 SkSTArray<NUM_INIT_BLOCK_PTRS, void*, true> fBlocks;
236 size_t fBlockSize;
237 size_t fItemSize;
238 int fItemsPerBlock;
239 bool fOwnFirstBlock;
240 int fCount;
241 int fInsertionIndexInBlock;
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000242
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +0000243 typedef SkNoncopyable INHERITED;
reed@google.comac10a2d2010-12-22 21:39:39 +0000244};
245
bsalomonb3e3a952014-09-19 11:10:40 -0700246template <typename T> class GrTAllocator;
247template <typename T> void* operator new(size_t, GrTAllocator<T>*);
248
249template <typename T> class GrTAllocator : SkNoncopyable {
reed@google.comac10a2d2010-12-22 21:39:39 +0000250public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400251 virtual ~GrTAllocator() { this->reset(); }
bsalomon@google.coma55847b2011-04-20 15:47:04 +0000252
reed@google.comac10a2d2010-12-22 21:39:39 +0000253 /**
254 * Create an allocator
255 *
256 * @param itemsPerBlock the number of items to allocate at once
reed@google.comac10a2d2010-12-22 21:39:39 +0000257 */
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000258 explicit GrTAllocator(int itemsPerBlock)
halcanary96fcdcc2015-08-27 07:41:13 -0700259 : fAllocator(sizeof(T), itemsPerBlock, nullptr) {}
bsalomon@google.coma55847b2011-04-20 15:47:04 +0000260
261 /**
reed@google.comac10a2d2010-12-22 21:39:39 +0000262 * Adds an item and returns it.
263 *
264 * @return the added item.
265 */
266 T& push_back() {
267 void* item = fAllocator.push_back();
bsalomon49f085d2014-09-05 13:34:00 -0700268 SkASSERT(item);
halcanary385fe4d2015-08-26 13:07:48 -0700269 new (item) T;
reed@google.comac10a2d2010-12-22 21:39:39 +0000270 return *(T*)item;
271 }
bsalomon@google.com4fa66942011-09-20 19:06:12 +0000272
273 T& push_back(const T& t) {
274 void* item = fAllocator.push_back();
bsalomon49f085d2014-09-05 13:34:00 -0700275 SkASSERT(item);
halcanary385fe4d2015-08-26 13:07:48 -0700276 new (item) T(t);
bsalomon@google.com4fa66942011-09-20 19:06:12 +0000277 return *(T*)item;
278 }
279
Brian Salomonbc6b99d2017-01-11 10:32:34 -0500280 template <typename... Args> T& emplace_back(Args&&... args) {
281 void* item = fAllocator.push_back();
282 SkASSERT(item);
283 new (item) T(std::forward<Args>(args)...);
284 return *(T*)item;
285 }
286
reed@google.comac10a2d2010-12-22 21:39:39 +0000287 /**
bsalomona1ae66d2014-09-05 06:13:43 -0700288 * Remove the last item, only call if count() != 0
289 */
290 void pop_back() {
291 this->back().~T();
292 fAllocator.pop_back();
293 }
294
295 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700296 * Removes all added items.
reed@google.comac10a2d2010-12-22 21:39:39 +0000297 */
298 void reset() {
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000299 int c = fAllocator.count();
300 for (int i = 0; i < c; ++i) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000301 ((T*)fAllocator[i])->~T();
302 }
303 fAllocator.reset();
304 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000305
reed@google.comac10a2d2010-12-22 21:39:39 +0000306 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700307 * Returns the item count.
reed@google.comac10a2d2010-12-22 21:39:39 +0000308 */
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000309 int count() const {
reed@google.comac10a2d2010-12-22 21:39:39 +0000310 return fAllocator.count();
311 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000312
reed@google.comac10a2d2010-12-22 21:39:39 +0000313 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700314 * Is the count 0?
reed@google.comac10a2d2010-12-22 21:39:39 +0000315 */
316 bool empty() const { return fAllocator.empty(); }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000317
reed@google.comac10a2d2010-12-22 21:39:39 +0000318 /**
Chris Daltond0d409d2018-06-08 11:51:39 -0600319 * Access first item, only call if count() != 0
320 */
321 T& front() {
322 return *(T*)fAllocator.front();
323 }
324
325 /**
326 * Access first item, only call if count() != 0
327 */
328 const T& front() const {
329 return *(T*)fAllocator.front();
330 }
331
332 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700333 * Access last item, only call if count() != 0
reed@google.comac10a2d2010-12-22 21:39:39 +0000334 */
335 T& back() {
336 return *(T*)fAllocator.back();
337 }
338
339 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700340 * Access last item, only call if count() != 0
reed@google.comac10a2d2010-12-22 21:39:39 +0000341 */
342 const T& back() const {
343 return *(const T*)fAllocator.back();
344 }
345
346 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700347 * Iterates through the allocator. This is faster than using operator[] when walking linearly
348 * through the allocator.
349 */
350 class Iter {
351 public:
352 /**
353 * Initializes the iterator. next() must be called before get() or ops * and ->.
354 */
355 Iter(const GrTAllocator* allocator) : fImpl(&allocator->fAllocator) {}
356
357 /**
358 * Advances the iterator. Iteration is finished when next() returns false.
359 */
360 bool next() { return fImpl.next(); }
361
362 /**
363 * Gets the current iterator value. Call next() at least once before calling. Don't call
364 * after next() returns false.
365 */
bsalomone1bc1c62014-07-02 13:44:57 -0700366 T* get() const { return (T*) fImpl.get(); }
bsalomonbce3d6d2014-07-02 07:54:42 -0700367
368 /**
369 * Convenience operators. Same rules for calling apply as get().
370 */
bsalomone1bc1c62014-07-02 13:44:57 -0700371 T& operator*() const { return *this->get(); }
372 T* operator->() const { return this->get(); }
bsalomonbce3d6d2014-07-02 07:54:42 -0700373
374 private:
375 GrAllocator::Iter fImpl;
376 };
377
378 /**
379 * Access item by index.
rmistry@google.comd6176b02012-08-23 18:14:13 +0000380 */
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000381 T& operator[] (int i) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000382 return *(T*)(fAllocator[i]);
383 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000384
reed@google.comac10a2d2010-12-22 21:39:39 +0000385 /**
bsalomonbce3d6d2014-07-02 07:54:42 -0700386 * Access item by index.
reed@google.comac10a2d2010-12-22 21:39:39 +0000387 */
bsalomon@google.com6a77cc52011-04-28 17:33:34 +0000388 const T& operator[] (int i) const {
reed@google.comac10a2d2010-12-22 21:39:39 +0000389 return *(const T*)(fAllocator[i]);
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000390 }
391
392protected:
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000393 /*
394 * Set first block of memory to write into. Must be called before any other methods.
395 *
396 * @param initialBlock optional memory to use for the first block.
397 * Must be at least size(T)*itemsPerBlock sized.
398 * Caller is responsible for freeing this memory.
399 */
400 void setInitialBlock(void* initialBlock) {
401 fAllocator.setInitialBlock(initialBlock);
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000402 }
403
404private:
bsalomonb3e3a952014-09-19 11:10:40 -0700405 friend void* operator new<T>(size_t, GrTAllocator*);
406
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000407 GrAllocator fAllocator;
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +0000408 typedef SkNoncopyable INHERITED;
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000409};
410
411template <int N, typename T> class GrSTAllocator : public GrTAllocator<T> {
412private:
413 typedef GrTAllocator<T> INHERITED;
414
415public:
commit-bot@chromium.org845da772013-12-02 22:32:58 +0000416 GrSTAllocator() : INHERITED(N) {
417 this->setInitialBlock(fStorage.get());
bsalomon@google.com4b90c622011-09-28 17:52:15 +0000418 }
419
420private:
421 SkAlignedSTStorage<N, T> fStorage;
reed@google.comac10a2d2010-12-22 21:39:39 +0000422};
423
bsalomonb3e3a952014-09-19 11:10:40 -0700424template <typename T> void* operator new(size_t size, GrTAllocator<T>* allocator) {
425 return allocator->fAllocator.push_back();
426}
427
428// Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete
429// to match the op new silences warnings about missing op delete when a constructor throws an
430// exception.
431template <typename T> void operator delete(void*, GrTAllocator<T>*) {
djsollenf2b340f2016-01-29 08:51:04 -0800432 SK_ABORT("Invalid Operation");
bsalomonb3e3a952014-09-19 11:10:40 -0700433}
434
435#define GrNEW_APPEND_TO_ALLOCATOR(allocator_ptr, type_name, args) \
436 new (allocator_ptr) type_name args
437
reed@google.comac10a2d2010-12-22 21:39:39 +0000438#endif