1/*
2 * Copyright 2014 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 GrTRecorder_DEFINED
9#define GrTRecorder_DEFINED
10
11#include "SkTypes.h"
12
13template<typename TBase, typename TAlign> class GrTRecorder;
14template<typename TItem> struct GrTRecorderAllocWrapper;
15
16/**
17 * Records a list of items with a common base type, optional associated data, and
18 * permanent memory addresses.
19 *
20 * This class preallocates its own chunks of memory for hosting objects, so new items can
21 * be created without excessive calls to malloc().
22 *
23 * To create a new item and append it to the back of the list, use the following macros:
24 *
25 *     GrNEW_APPEND_TO_RECORDER(recorder, SubclassName, (args))
26 *     GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, SubclassName, (args), sizeOfData)
27 *
28 * Upon reset or delete, the items are destructed in the same order they were received,
29 * not reverse (stack) order.
30 *
31 * @param TBase   Common base type of items in the list. If TBase is not a class with a
32 *                virtual destructor, the client is responsible for invoking any necessary
33 *                destructors.
34 *
35 *                For now, any subclass used in the list must have the same start address
36 *                as TBase (or in other words, the types must be convertible via
37 *                reinterpret_cast<>). Classes with multiple inheritance (or any subclass
38 *                on an obscure compiler) may not be compatible. This is runtime asserted
39 *                in debug builds.
40 *
41 * @param TAlign  A type whose size is the desired memory alignment for object allocations.
42 *                This should be the largest known alignment requirement for all objects
43 *                that may be stored in the list.
44 */
45template<typename TBase, typename TAlign> class GrTRecorder : SkNoncopyable {
46public:
47    class Iter;
48    class ReverseIter;
49
50    /**
51     * Create a recorder.
52     *
53     * @param initialSizeInBytes  The amount of memory reserved by the recorder initially,
54                                  and after calls to reset().
55     */
56    GrTRecorder(int initialSizeInBytes)
57        : fHeadBlock(MemBlock::Alloc(LengthOf(initialSizeInBytes), nullptr)),
58          fTailBlock(fHeadBlock),
59          fLastItem(nullptr) {}
60
61    ~GrTRecorder() {
62        this->reset();
63        MemBlock::Free(fHeadBlock);
64    }
65
66    bool empty() { return !fLastItem; }
67
68    TBase& back() {
69        SkASSERT(!this->empty());
70        return *reinterpret_cast<TBase*>(fLastItem);
71    }
72
73    /**
74     * Removes and destroys the last block added to the recorder. It may not be called when the
75     * recorder is empty.
76     */
77    void pop_back();
78
79    /**
80     * Destruct all items in the list and reset to empty.
81     */
82    void reset();
83
84    /**
85     * Retrieve the extra data associated with an item that was allocated using
86     * GrNEW_APPEND_WITH_DATA_TO_RECORDER().
87     *
88     * @param item  The item whose data to retrieve. The pointer must be of the same type
89     *              that was allocated initally; it can't be a pointer to a base class.
90     *
91     * @return The item's associated data.
92     */
93    template<typename TItem> static const void* GetDataForItem(const TItem* item) {
94        const TAlign* ptr = reinterpret_cast<const TAlign*>(item);
95        return &ptr[length_of<TItem>::kValue];
96    }
97    template<typename TItem> static void* GetDataForItem(TItem* item) {
98        TAlign* ptr = reinterpret_cast<TAlign*>(item);
99        return &ptr[length_of<TItem>::kValue];
100    }
101
102private:
103    template<typename TItem> struct length_of {
104        enum { kValue = (sizeof(TItem) + sizeof(TAlign) - 1) / sizeof(TAlign) };
105    };
106    static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); }
107
108    struct Header {
109        int fTotalLength; // The length of an entry including header, item, and data in TAligns.
110        int fPrevLength;  // Same but for the previous entry. Used for iterating backwards.
111    };
112    template<typename TItem> void* alloc_back(int dataLength);
113
114    struct MemBlock : SkNoncopyable {
115        /** Allocates a new block and appends it to prev if not nullptr. The length param is in units
116            of TAlign. */
117        static MemBlock* Alloc(int length, MemBlock* prev) {
118            MemBlock* block = reinterpret_cast<MemBlock*>(
119                sk_malloc_throw(sizeof(TAlign) * (length_of<MemBlock>::kValue + length)));
120            block->fLength = length;
121            block->fBack = 0;
122            block->fNext = nullptr;
123            block->fPrev = prev;
124            if (prev) {
125                SkASSERT(nullptr == prev->fNext);
126                prev->fNext = block;
127            }
128            return block;
129        }
130
131        // Frees from this block forward. Also adjusts prev block's next ptr.
132        static void Free(MemBlock* block) {
133            if (block && block->fPrev) {
134                SkASSERT(block->fPrev->fNext == block);
135                block->fPrev->fNext = nullptr;
136            }
137            while (block) {
138                MemBlock* next = block->fNext;
139                sk_free(block);
140                block = next;
141            }
142        }
143
144        TAlign& operator [](int i) {
145            return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue + i];
146        }
147
148        int       fLength; // Length in units of TAlign of the block.
149        int       fBack;   // Offset, in TAligns, to unused portion of the memory block.
150        MemBlock* fNext;
151        MemBlock* fPrev;
152    };
153    MemBlock* const fHeadBlock;
154    MemBlock* fTailBlock;
155
156    void*    fLastItem; // really a ptr to TBase
157
158    template<typename TItem> friend struct GrTRecorderAllocWrapper;
159
160    template <typename UBase, typename UAlign, typename UItem>
161    friend void* operator new(size_t, GrTRecorder<UBase, UAlign>&,
162                              const GrTRecorderAllocWrapper<UItem>&);
163
164    friend class Iter;
165    friend class ReverseIter;
166};
167
168////////////////////////////////////////////////////////////////////////////////
169
170template<typename TBase, typename TAlign>
171void GrTRecorder<TBase, TAlign>::pop_back() {
172    SkASSERT(fLastItem);
173    Header* header = reinterpret_cast<Header*>(
174        reinterpret_cast<TAlign*>(fLastItem) - length_of<Header>::kValue);
175    fTailBlock->fBack -= header->fTotalLength;
176    reinterpret_cast<TBase*>(fLastItem)->~TBase();
177
178    int lastItemLength = header->fPrevLength;
179
180    if (!header->fPrevLength) {
181        // We popped the first entry in the recorder.
182        SkASSERT(0 == fTailBlock->fBack);
183        fLastItem = nullptr;
184        return;
185    }
186    while (!fTailBlock->fBack) {
187        // We popped the last entry in a block that isn't the head block. Move back a block but
188        // don't free it since we'll probably grow into it shortly.
189        fTailBlock = fTailBlock->fPrev;
190        SkASSERT(fTailBlock);
191    }
192    fLastItem = &(*fTailBlock)[fTailBlock->fBack - lastItemLength + length_of<Header>::kValue];
193}
194
195template<typename TBase, typename TAlign>
196template<typename TItem>
197void* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) {
198    // Find the header of the previous entry and get its length. We need to store that in the new
199    // header for backwards iteration (pop_back()).
200    int prevLength = 0;
201    if (fLastItem) {
202        Header* lastHeader = reinterpret_cast<Header*>(
203            reinterpret_cast<TAlign*>(fLastItem) - length_of<Header>::kValue);
204        prevLength = lastHeader->fTotalLength;
205    }
206
207    const int totalLength = length_of<Header>::kValue + length_of<TItem>::kValue + dataLength;
208
209    // Check if there is room in the current block and if not walk to next (allocating if
210    // necessary). Note that pop_back() and reset() can leave the recorder in a state where it
211    // has preallocated blocks hanging off the tail that are currently unused.
212    while (fTailBlock->fBack + totalLength > fTailBlock->fLength) {
213        if (!fTailBlock->fNext) {
214            fTailBlock = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, totalLength), fTailBlock);
215        } else {
216            fTailBlock = fTailBlock->fNext;
217        }
218        SkASSERT(0 == fTailBlock->fBack);
219    }
220
221    Header* header = reinterpret_cast<Header*>(&(*fTailBlock)[fTailBlock->fBack]);
222    void* rawPtr = &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kValue];
223
224    header->fTotalLength = totalLength;
225    header->fPrevLength = prevLength;
226    fLastItem = rawPtr;
227    fTailBlock->fBack += totalLength;
228
229    // FIXME: We currently require that the base and subclass share the same start address.
230    // This is not required by the C++ spec, and is likely to not be true in the case of
231    // multiple inheritance or a base class that doesn't have virtual methods (when the
232    // subclass does). It would be ideal to find a more robust solution that comes at no
233    // extra cost to performance or code generality.
234    SkDEBUGCODE(void* baseAddr = fLastItem;
235                void* subclassAddr = rawPtr);
236    SkASSERT(baseAddr == subclassAddr);
237
238    return rawPtr;
239}
240
241/**
242 * Iterates through a recorder from front to back. The initial state of the iterator is
243 * to not have the front item loaded yet; next() must be called first. Usage model:
244 *
245 *    GrTRecorder<TBase, TAlign>::Iter iter(recorder);
246 *    while (iter.next()) {
247 *        iter->doSomething();
248 *    }
249 */
250template<typename TBase, typename TAlign>
251class GrTRecorder<TBase, TAlign>::Iter {
252public:
253    Iter(GrTRecorder& recorder) : fBlock(recorder.fHeadBlock), fPosition(0), fItem(nullptr) {}
254
255    bool next() {
256        while (fPosition >= fBlock->fBack) {
257            SkASSERT(fPosition == fBlock->fBack);
258            if (!fBlock->fNext) {
259                return false;
260            }
261            fBlock = fBlock->fNext;
262            fPosition = 0;
263        }
264
265        Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]);
266        fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header>::kValue]);
267        fPosition += header->fTotalLength;
268        return true;
269    }
270
271    TBase* get() const {
272        SkASSERT(fItem);
273        return fItem;
274    }
275
276    TBase* operator->() const { return this->get(); }
277
278private:
279    MemBlock* fBlock;
280    int       fPosition;
281    TBase*    fItem;
282};
283
284/**
285 * Iterates through a recorder in reverse, from back to front. This version mirrors "Iter",
286 * so the initial state is to have recorder.back() loaded already. (Note that this will
287 * assert if the recorder is empty.) Usage model:
288 *
289 *    GrTRecorder<TBase, TAlign>::ReverseIter reverseIter(recorder);
290 *    do {
291 *        reverseIter->doSomething();
292 *    } while (reverseIter.previous());
293 */
294template<typename TBase, typename TAlign>
295class GrTRecorder<TBase, TAlign>::ReverseIter {
296public:
297    ReverseIter(GrTRecorder& recorder)
298        : fBlock(recorder.fTailBlock),
299          fItem(&recorder.back()) {
300        Header* lastHeader = reinterpret_cast<Header*>(
301            reinterpret_cast<TAlign*>(fItem) - length_of<Header>::kValue);
302        fPosition = fBlock->fBack - lastHeader->fTotalLength;
303    }
304
305    bool previous() {
306        Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]);
307
308        while (0 == fPosition) {
309            if (!fBlock->fPrev) {
310                // We've reached the front of the recorder.
311                return false;
312            }
313            fBlock = fBlock->fPrev;
314            fPosition = fBlock->fBack;
315        }
316
317        fPosition -= header->fPrevLength;
318        SkASSERT(fPosition >= 0);
319
320        fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header>::kValue]);
321        return true;
322    }
323
324    TBase* get() const { return fItem; }
325    TBase* operator->() const { return this->get(); }
326
327private:
328    MemBlock* fBlock;
329    int       fPosition;
330    TBase*    fItem;
331};
332
333template<typename TBase, typename TAlign>
334void GrTRecorder<TBase, TAlign>::reset() {
335    Iter iter(*this);
336    while (iter.next()) {
337        iter->~TBase();
338    }
339
340    // Assume the next time this recorder fills up it will use approximately the same
341    // amount of space as last time. Leave enough space for up to ~50% growth; free
342    // everything else.
343    if (fTailBlock->fBack <= fTailBlock->fLength / 2) {
344        MemBlock::Free(fTailBlock->fNext);
345    } else if (fTailBlock->fNext) {
346        MemBlock::Free(fTailBlock->fNext->fNext);
347        fTailBlock->fNext->fNext = nullptr;
348    }
349
350    for (MemBlock* block = fHeadBlock; block; block = block->fNext) {
351        block->fBack = 0;
352    }
353
354    fTailBlock = fHeadBlock;
355    fLastItem = nullptr;
356}
357
358////////////////////////////////////////////////////////////////////////////////
359
360template<typename TItem> struct GrTRecorderAllocWrapper {
361    GrTRecorderAllocWrapper() : fDataLength(0) {}
362
363    template <typename TBase, typename TAlign>
364    GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData)
365        : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {}
366
367    const int fDataLength;
368};
369
370template <typename TBase, typename TAlign, typename TItem>
371void* operator new(size_t size, GrTRecorder<TBase, TAlign>& recorder,
372                   const GrTRecorderAllocWrapper<TItem>& wrapper) {
373    SkASSERT(size == sizeof(TItem));
374    return recorder.template alloc_back<TItem>(wrapper.fDataLength);
375}
376
377template <typename TBase, typename TAlign, typename TItem>
378void operator delete(void*, GrTRecorder<TBase, TAlign>&, const GrTRecorderAllocWrapper<TItem>&) {
379    // We only provide an operator delete to work around compiler warnings that can come
380    // up for an unmatched operator new when compiling with exceptions.
381    SK_ABORT("Invalid Operation");
382}
383
384#define GrNEW_APPEND_TO_RECORDER(recorder, type_name, args) \
385    (new (recorder, GrTRecorderAllocWrapper<type_name>()) type_name args)
386
387#define GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, type_name, args, size_of_data) \
388    (new (recorder, GrTRecorderAllocWrapper<type_name>(recorder, size_of_data)) type_name args)
389
390#endif
391