1 /*
2  * 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.
6  */
7 
8 #ifndef GrAllocator_DEFINED
9 #define GrAllocator_DEFINED
10 
11 #include "GrConfig.h"
12 #include "GrTypes.h"
13 #include "SkNoncopyable.h"
14 #include "SkTArray.h"
15 #include "SkTypes.h"
16 #include <new>
17 
18 class GrAllocator : SkNoncopyable {
19 public:
20     ~GrAllocator() { this->reset(); }
21 
22     /**
23      * Create an allocator
24      *
25      * @param   itemSize        the size of each item to allocate
26      * @param   itemsPerBlock   the number of items to allocate at once
27      * @param   initialBlock    optional memory to use for the first block.
28      *                          Must be at least itemSize*itemsPerBlock sized.
29      *                          Caller is responsible for freeing this memory.
30      */
31     GrAllocator(size_t itemSize, int itemsPerBlock, void* initialBlock)
32         : fItemSize(itemSize)
33         , fItemsPerBlock(itemsPerBlock)
34         , fOwnFirstBlock(nullptr == initialBlock)
35         , fCount(0)
36         , fInsertionIndexInBlock(0) {
37         SkASSERT(itemsPerBlock > 0);
38         fBlockSize = fItemSize * fItemsPerBlock;
39         if (fOwnFirstBlock) {
40             // This force us to allocate a new block on push_back().
41             fInsertionIndexInBlock = fItemsPerBlock;
42         } else {
43             fBlocks.push_back() = initialBlock;
44             fInsertionIndexInBlock = 0;
45         }
46     }
47 
48     /**
49      * Adds an item and returns pointer to it.
50      *
51      * @return pointer to the added item.
52      */
53     void* push_back() {
54         // we always have at least one block
55         if (fItemsPerBlock == fInsertionIndexInBlock) {
56             fBlocks.push_back() = sk_malloc_throw(fBlockSize);
57             fInsertionIndexInBlock = 0;
58         }
59         void* ret = (char*)fBlocks.back() + fItemSize * fInsertionIndexInBlock;
60         ++fCount;
61         ++fInsertionIndexInBlock;
62         return ret;
63     }
64 
65     /**
66      * Remove the last item, only call if count() != 0
67      */
68     void pop_back() {
69         SkASSERT(fCount);
70         SkASSERT(fInsertionIndexInBlock > 0);
71         --fInsertionIndexInBlock;
72         --fCount;
73         if (0 == fInsertionIndexInBlock) {
74             // Never delete the first block
75             if (fBlocks.count() > 1) {
76                 sk_free(fBlocks.back());
77                 fBlocks.pop_back();
78                 fInsertionIndexInBlock = fItemsPerBlock;
79             }
80         }
81     }
82 
83     /**
84      * Removes all added items.
85      */
86     void reset() {
87         int firstBlockToFree = fOwnFirstBlock ? 0 : 1;
88         for (int i = firstBlockToFree; i < fBlocks.count(); ++i) {
89             sk_free(fBlocks[i]);
90         }
91         if (fOwnFirstBlock) {
92             fBlocks.reset();
93             // This force us to allocate a new block on push_back().
94             fInsertionIndexInBlock = fItemsPerBlock;
95         } else {
96             fBlocks.pop_back_n(fBlocks.count() - 1);
97             fInsertionIndexInBlock = 0;
98         }
99         fCount = 0;
100     }
101 
102     /**
103      * Returns the item count.
104      */
105     int count() const {
106         return fCount;
107     }
108 
109     /**
110      * Is the count 0?
111      */
112     bool empty() const { return 0 == fCount; }
113 
114     /**
115      * Access first item, only call if count() != 0
116      */
117     void* front() {
118         SkASSERT(fCount);
119         SkASSERT(fInsertionIndexInBlock > 0);
120         return (char*)(fBlocks.front());
121     }
122 
123     /**
124      * Access first item, only call if count() != 0
125      */
126     const void* front() const {
127         SkASSERT(fCount);
128         SkASSERT(fInsertionIndexInBlock > 0);
129         return (const char*)(fBlocks.front());
130     }
131 
132     /**
133      * Access last item, only call if count() != 0
134      */
135     void* back() {
136         SkASSERT(fCount);
137         SkASSERT(fInsertionIndexInBlock > 0);
138         return (char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
139     }
140 
141     /**
142      * Access last item, only call if count() != 0
143      */
144     const void* back() const {
145         SkASSERT(fCount);
146         SkASSERT(fInsertionIndexInBlock > 0);
147         return (const char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize;
148     }
149 
150     /**
151      * Iterates through the allocator. This is faster than using operator[] when walking linearly
152      * through the allocator.
153      */
154     class Iter {
155     public:
156         /**
157          * Initializes the iterator. next() must be called before get().
158          */
159         Iter(const GrAllocator* allocator)
160             : fAllocator(allocator)
161             , fBlockIndex(-1)
162             , fIndexInBlock(allocator->fItemsPerBlock - 1)
163             , fItemIndex(-1) {}
164 
165         /**
166          * Advances the iterator. Iteration is finished when next() returns false.
167          */
168         bool next() {
169             ++fIndexInBlock;
170             ++fItemIndex;
171             if (fIndexInBlock == fAllocator->fItemsPerBlock) {
172                 ++fBlockIndex;
173                 fIndexInBlock = 0;
174             }
175             return fItemIndex < fAllocator->fCount;
176         }
177 
178         /**
179          * Gets the current iterator value. Call next() at least once before calling. Don't call
180          * after next() returns false.
181          */
182         void* get() const {
183             SkASSERT(fItemIndex >= 0 && fItemIndex < fAllocator->fCount);
184             return (char*) fAllocator->fBlocks[fBlockIndex] + fIndexInBlock * fAllocator->fItemSize;
185         }
186 
187     private:
188         const GrAllocator* fAllocator;
189         int                fBlockIndex;
190         int                fIndexInBlock;
191         int                fItemIndex;
192     };
193 
194     /**
195      * Access item by index.
196      */
197     void* operator[] (int i) {
198         SkASSERT(i >= 0 && i < fCount);
199         return (char*)fBlocks[i / fItemsPerBlock] +
200                fItemSize * (i % fItemsPerBlock);
201     }
202 
203     /**
204      * Access item by index.
205      */
206     const void* operator[] (int i) const {
207         SkASSERT(i >= 0 && i < fCount);
208         return (const char*)fBlocks[i / fItemsPerBlock] +
209                fItemSize * (i % fItemsPerBlock);
210     }
211 
212 protected:
213     /**
214      * Set first block of memory to write into.  Must be called before any other methods.
215      * This requires that you have passed nullptr in the constructor.
216      *
217      * @param   initialBlock    optional memory to use for the first block.
218      *                          Must be at least itemSize*itemsPerBlock sized.
219      *                          Caller is responsible for freeing this memory.
220      */
221     void setInitialBlock(void* initialBlock) {
222         SkASSERT(0 == fCount);
223         SkASSERT(0 == fBlocks.count());
224         SkASSERT(fItemsPerBlock == fInsertionIndexInBlock);
225         fOwnFirstBlock = false;
226         fBlocks.push_back() = initialBlock;
227         fInsertionIndexInBlock = 0;
228     }
229 
230     // For access to above function.
231     template <typename T> friend class GrTAllocator;
232 
233 private:
234     static const int NUM_INIT_BLOCK_PTRS = 8;
235 
236     SkSTArray<NUM_INIT_BLOCK_PTRS, void*, true>   fBlocks;
237     size_t                                        fBlockSize;
238     size_t                                        fItemSize;
239     int                                           fItemsPerBlock;
240     bool                                          fOwnFirstBlock;
241     int                                           fCount;
242     int                                           fInsertionIndexInBlock;
243 
244     typedef SkNoncopyable INHERITED;
245 };
246 
247 template <typename T> class GrTAllocator;
248 template <typename T> void* operator new(size_t, GrTAllocator<T>*);
249 
250 template <typename T> class GrTAllocator : SkNoncopyable {
251 public:
252     virtual ~GrTAllocator() { this->reset(); }
253 
254     /**
255      * Create an allocator
256      *
257      * @param   itemsPerBlock   the number of items to allocate at once
258      */
259     explicit GrTAllocator(int itemsPerBlock)
260         : fAllocator(sizeof(T), itemsPerBlock, nullptr) {}
261 
262     /**
263      * Adds an item and returns it.
264      *
265      * @return the added item.
266      */
267     T& push_back() {
268         void* item = fAllocator.push_back();
269         SkASSERT(item);
270         new (item) T;
271         return *(T*)item;
272     }
273 
274     T& push_back(const T& t) {
275         void* item = fAllocator.push_back();
276         SkASSERT(item);
277         new (item) T(t);
278         return *(T*)item;
279     }
280 
281     template <typename... Args> T& emplace_back(Args&&... args) {
282         void* item = fAllocator.push_back();
283         SkASSERT(item);
284         new (item) T(std::forward<Args>(args)...);
285         return *(T*)item;
286     }
287 
288     /**
289      * Remove the last item, only call if count() != 0
290      */
291     void pop_back() {
292         this->back().~T();
293         fAllocator.pop_back();
294     }
295 
296     /**
297      * Removes all added items.
298      */
299     void reset() {
300         int c = fAllocator.count();
301         for (int i = 0; i < c; ++i) {
302             ((T*)fAllocator[i])->~T();
303         }
304         fAllocator.reset();
305     }
306 
307     /**
308      * Returns the item count.
309      */
310     int count() const {
311         return fAllocator.count();
312     }
313 
314     /**
315      * Is the count 0?
316      */
317     bool empty() const { return fAllocator.empty(); }
318 
319     /**
320      * Access first item, only call if count() != 0
321      */
322     T& front() {
323         return *(T*)fAllocator.front();
324     }
325 
326     /**
327      * Access first item, only call if count() != 0
328      */
329     const T& front() const {
330         return *(T*)fAllocator.front();
331     }
332 
333     /**
334      * Access last item, only call if count() != 0
335      */
336     T& back() {
337         return *(T*)fAllocator.back();
338     }
339 
340     /**
341      * Access last item, only call if count() != 0
342      */
343     const T& back() const {
344         return *(const T*)fAllocator.back();
345     }
346 
347     /**
348      * Iterates through the allocator. This is faster than using operator[] when walking linearly
349      * through the allocator.
350      */
351     class Iter {
352     public:
353         /**
354          * Initializes the iterator. next() must be called before get() or ops * and ->.
355          */
356         Iter(const GrTAllocator* allocator) : fImpl(&allocator->fAllocator) {}
357 
358         /**
359          * Advances the iterator. Iteration is finished when next() returns false.
360          */
361         bool next() { return fImpl.next(); }
362 
363         /**
364          * Gets the current iterator value. Call next() at least once before calling. Don't call
365          * after next() returns false.
366          */
367         T* get() const { return (T*) fImpl.get(); }
368 
369         /**
370          * Convenience operators. Same rules for calling apply as get().
371          */
372         T& operator*() const { return *this->get(); }
373         T* operator->() const { return this->get(); }
374 
375     private:
376         GrAllocator::Iter fImpl;
377     };
378 
379     /**
380      * Access item by index.
381      */
382     T& operator[] (int i) {
383         return *(T*)(fAllocator[i]);
384     }
385 
386     /**
387      * Access item by index.
388      */
389     const T& operator[] (int i) const {
390         return *(const T*)(fAllocator[i]);
391     }
392 
393 protected:
394     /*
395      * Set first block of memory to write into.  Must be called before any other methods.
396      *
397      * @param   initialBlock    optional memory to use for the first block.
398      *                          Must be at least size(T)*itemsPerBlock sized.
399      *                          Caller is responsible for freeing this memory.
400      */
401     void setInitialBlock(void* initialBlock) {
402         fAllocator.setInitialBlock(initialBlock);
403     }
404 
405 private:
406     friend void* operator new<T>(size_t, GrTAllocator*);
407 
408     GrAllocator fAllocator;
409     typedef SkNoncopyable INHERITED;
410 };
411 
412 template <int N, typename T> class GrSTAllocator : public GrTAllocator<T> {
413 private:
414     typedef GrTAllocator<T> INHERITED;
415 
416 public:
417     GrSTAllocator() : INHERITED(N) {
418         this->setInitialBlock(fStorage.get());
419     }
420 
421 private:
422     SkAlignedSTStorage<N, T> fStorage;
423 };
424 
425 template <typename T> void* operator new(size_t size, GrTAllocator<T>* allocator) {
426     return allocator->fAllocator.push_back();
427 }
428 
429 // Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete
430 // to match the op new silences warnings about missing op delete when a constructor throws an
431 // exception.
432 template <typename T> void operator delete(void*, GrTAllocator<T>*) {
433     SK_ABORT("Invalid Operation");
434 }
435 
436 #define GrNEW_APPEND_TO_ALLOCATOR(allocator_ptr, type_name, args) \
437     new (allocator_ptr) type_name args
438 
439 #endif
440