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