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 "SkTArray.h"
14#include "SkTypes.h"
15
16class GrAllocator : public SkNoncopyable {
17public:
18    ~GrAllocator() {
19        reset();
20    }
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(NULL == initialBlock),
35            fCount(0) {
36        SkASSERT(itemsPerBlock > 0);
37        fBlockSize = fItemSize * fItemsPerBlock;
38        fBlocks.push_back() = initialBlock;
39        SkDEBUGCODE(if (!fOwnFirstBlock) {*((char*)initialBlock+fBlockSize-1)='a';} );
40    }
41
42    /*
43     * Set first block of memory to write into.  Must be called before any other methods.
44     * This requires that you have passed NULL in the constructor.
45     *
46     * @param   initialBlock    optional memory to use for the first block.
47     *                          Must be at least itemSize*itemsPerBlock sized.
48     *                          Caller is responsible for freeing this memory.
49     */
50    void setInitialBlock(void* initialBlock) {
51        SkASSERT(0 == fCount);
52        SkASSERT(1 == fBlocks.count());
53        SkASSERT(NULL == fBlocks.back());
54        fOwnFirstBlock = false;
55        fBlocks.back() = initialBlock;
56    }
57
58    /**
59     * Adds an item and returns pointer to it.
60     *
61     * @return pointer to the added item.
62     */
63    void* push_back() {
64        int indexInBlock = fCount % fItemsPerBlock;
65        // we always have at least one block
66        if (0 == indexInBlock) {
67            if (0 != fCount) {
68                fBlocks.push_back() = sk_malloc_throw(fBlockSize);
69            } else if (fOwnFirstBlock) {
70                fBlocks[0] = sk_malloc_throw(fBlockSize);
71            }
72        }
73        void* ret = (char*)fBlocks[fCount/fItemsPerBlock] +
74                    fItemSize * indexInBlock;
75        ++fCount;
76        return ret;
77    }
78
79    /**
80     * removes all added items
81     */
82    void reset() {
83        int blockCount = GrMax((unsigned)1,
84                               GrUIDivRoundUp(fCount, fItemsPerBlock));
85        for (int i = 1; i < blockCount; ++i) {
86            sk_free(fBlocks[i]);
87        }
88        if (fOwnFirstBlock) {
89            sk_free(fBlocks[0]);
90            fBlocks[0] = NULL;
91        }
92        fBlocks.pop_back_n(blockCount-1);
93        fCount = 0;
94    }
95
96    /**
97     * count of items
98     */
99    int count() const {
100        return fCount;
101    }
102
103    /**
104     * is the count 0
105     */
106    bool empty() const { return fCount == 0; }
107
108    /**
109     * access last item, only call if count() != 0
110     */
111    void* back() {
112        SkASSERT(fCount);
113        return (*this)[fCount-1];
114    }
115
116    /**
117     * access last item, only call if count() != 0
118     */
119    const void* back() const {
120        SkASSERT(fCount);
121        return (*this)[fCount-1];
122    }
123
124    /**
125     * access item by index.
126     */
127    void* operator[] (int i) {
128        SkASSERT(i >= 0 && i < fCount);
129        return (char*)fBlocks[i / fItemsPerBlock] +
130               fItemSize * (i % fItemsPerBlock);
131    }
132
133    /**
134     * access item by index.
135     */
136    const void* operator[] (int i) const {
137        SkASSERT(i >= 0 && i < fCount);
138        return (const char*)fBlocks[i / fItemsPerBlock] +
139               fItemSize * (i % fItemsPerBlock);
140    }
141
142private:
143    static const int NUM_INIT_BLOCK_PTRS = 8;
144
145    SkSTArray<NUM_INIT_BLOCK_PTRS, void*>   fBlocks;
146    size_t                                  fBlockSize;
147    size_t                                  fItemSize;
148    int                                     fItemsPerBlock;
149    bool                                    fOwnFirstBlock;
150    int                                     fCount;
151
152    typedef SkNoncopyable INHERITED;
153};
154
155template <typename T>
156class GrTAllocator : public SkNoncopyable {
157public:
158    virtual ~GrTAllocator() { this->reset(); };
159
160    /**
161     * Create an allocator
162     *
163     * @param   itemsPerBlock   the number of items to allocate at once
164     */
165    explicit GrTAllocator(int itemsPerBlock)
166        : fAllocator(sizeof(T), itemsPerBlock, NULL) {}
167
168    /**
169     * Adds an item and returns it.
170     *
171     * @return the added item.
172     */
173    T& push_back() {
174        void* item = fAllocator.push_back();
175        SkASSERT(NULL != item);
176        SkNEW_PLACEMENT(item, T);
177        return *(T*)item;
178    }
179
180    T& push_back(const T& t) {
181        void* item = fAllocator.push_back();
182        SkASSERT(NULL != item);
183        SkNEW_PLACEMENT_ARGS(item, T, (t));
184        return *(T*)item;
185    }
186
187    /**
188     * removes all added items
189     */
190    void reset() {
191        int c = fAllocator.count();
192        for (int i = 0; i < c; ++i) {
193            ((T*)fAllocator[i])->~T();
194        }
195        fAllocator.reset();
196    }
197
198    /**
199     * count of items
200     */
201    int count() const {
202        return fAllocator.count();
203    }
204
205    /**
206     * is the count 0
207     */
208    bool empty() const { return fAllocator.empty(); }
209
210    /**
211     * access last item, only call if count() != 0
212     */
213    T& back() {
214        return *(T*)fAllocator.back();
215    }
216
217    /**
218     * access last item, only call if count() != 0
219     */
220    const T& back() const {
221        return *(const T*)fAllocator.back();
222    }
223
224    /**
225     * access item by index.
226     */
227    T& operator[] (int i) {
228        return *(T*)(fAllocator[i]);
229    }
230
231    /**
232     * access item by index.
233     */
234    const T& operator[] (int i) const {
235        return *(const T*)(fAllocator[i]);
236    }
237
238protected:
239    /*
240     * Set first block of memory to write into.  Must be called before any other methods.
241     *
242     * @param   initialBlock    optional memory to use for the first block.
243     *                          Must be at least size(T)*itemsPerBlock sized.
244     *                          Caller is responsible for freeing this memory.
245     */
246    void setInitialBlock(void* initialBlock) {
247        fAllocator.setInitialBlock(initialBlock);
248    }
249
250private:
251    GrAllocator fAllocator;
252    typedef SkNoncopyable INHERITED;
253};
254
255template <int N, typename T> class GrSTAllocator : public GrTAllocator<T> {
256private:
257    typedef GrTAllocator<T> INHERITED;
258
259public:
260    GrSTAllocator() : INHERITED(N) {
261        this->setInitialBlock(fStorage.get());
262    }
263
264private:
265    SkAlignedSTStorage<N, T> fStorage;
266};
267
268#endif
269