2 * Copyright 2014 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #ifndef GrTRecorder_DEFINED
9 #define GrTRecorder_DEFINED
11 #include "SkTemplates.h"
14 template<typename TBase, typename TAlign> class GrTRecorder;
15 template<typename TItem> struct GrTRecorderAllocWrapper;
18 * Records a list of items with a common base type, optional associated data, and
19 * permanent memory addresses.
21 * This class preallocates its own chunks of memory for hosting objects, so new items can
22 * be created without excessive calls to malloc().
24 * To create a new item and append it to the back of the list, use the following macros:
26 * GrNEW_APPEND_TO_RECORDER(recorder, SubclassName, (args))
27 * GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, SubclassName, (args), sizeOfData)
29 * Upon reset or delete, the items are destructed in the same order they were received,
30 * not reverse (stack) order.
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
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
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.
46 template<typename TBase, typename TAlign> class GrTRecorder : SkNoncopyable {
53 * @param initialSizeInBytes The amount of memory reserved by the recorder initially,
54 and after calls to reset().
56 GrTRecorder(int initialSizeInBytes)
57 : fHeadBlock(MemBlock::Alloc(LengthOf(initialSizeInBytes))),
58 fTailBlock(fHeadBlock),
63 MemBlock::Free(fHeadBlock);
66 bool empty() { return !fLastItem; }
69 SkASSERT(!this->empty());
74 * Destruct all items in the list and reset to empty.
79 * Retrieve the extra data associated with an item that was allocated using
80 * GrNEW_APPEND_WITH_DATA_TO_RECORDER().
82 * @param item The item whose data to retrieve. The pointer must be of the same type
83 * that was allocated initally; it can't be a pointer to a base class.
85 * @return The item's associated data.
87 template<typename TItem> static const void* GetDataForItem(const TItem* item) {
88 const TAlign* ptr = reinterpret_cast<const TAlign*>(item);
89 return &ptr[length_of<TItem>::kValue];
91 template<typename TItem> static void* GetDataForItem(TItem* item) {
92 TAlign* ptr = reinterpret_cast<TAlign*>(item);
93 return &ptr[length_of<TItem>::kValue];
97 template<typename TItem> struct length_of {
98 enum { kValue = (sizeof(TItem) + sizeof(TAlign) - 1) / sizeof(TAlign) };
100 static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); }
105 template<typename TItem> TItem* alloc_back(int dataLength);
107 struct MemBlock : SkNoncopyable {
108 static MemBlock* Alloc(int length) {
109 MemBlock* block = reinterpret_cast<MemBlock*>(
110 sk_malloc_throw(sizeof(TAlign) * (length_of<MemBlock>::kValue + length)));
111 block->fLength = length;
117 static void Free(MemBlock* block) {
125 TAlign& operator [](int i) {
126 return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue + i];
133 MemBlock* const fHeadBlock;
134 MemBlock* fTailBlock;
138 template<typename TItem> friend struct GrTRecorderAllocWrapper;
140 template <typename UBase, typename UAlign, typename UItem>
141 friend void* operator new(size_t, GrTRecorder<UBase, UAlign>&,
142 const GrTRecorderAllocWrapper<UItem>&);
147 ////////////////////////////////////////////////////////////////////////////////
149 template<typename TBase, typename TAlign>
150 template<typename TItem>
151 TItem* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) {
152 const int totalLength = length_of<Header>::kValue + length_of<TItem>::kValue + dataLength;
154 if (fTailBlock->fBack + totalLength > fTailBlock->fLength) {
155 SkASSERT(!fTailBlock->fNext);
156 fTailBlock->fNext = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, totalLength));
157 fTailBlock = fTailBlock->fNext;
160 Header* header = reinterpret_cast<Header*>(&(*fTailBlock)[fTailBlock->fBack]);
161 TItem* rawPtr = reinterpret_cast<TItem*>(
162 &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kValue]);
164 header->fTotalLength = totalLength;
166 fTailBlock->fBack += totalLength;
168 // FIXME: We currently require that the base and subclass share the same start address.
169 // This is not required by the C++ spec, and is likely to not be true in the case of
170 // multiple inheritance or a base class that doesn't have virtual methods (when the
171 // subclass does). It would be ideal to find a more robust solution that comes at no
172 // extra cost to performance or code generality.
173 SkDEBUGCODE(void* baseAddr = fLastItem;
174 void* subclassAddr = rawPtr);
175 SkASSERT(baseAddr == subclassAddr);
180 template<typename TBase, typename TAlign>
181 class GrTRecorder<TBase, TAlign>::Iter {
183 Iter(GrTRecorder& recorder) : fBlock(recorder.fHeadBlock), fPosition(0), fItem(NULL) {}
186 if (fPosition >= fBlock->fBack) {
187 SkASSERT(fPosition == fBlock->fBack);
188 if (!fBlock->fNext) {
191 SkASSERT(0 != fBlock->fNext->fBack);
192 fBlock = fBlock->fNext;
196 Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]);
197 fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header>::kValue]);
198 fPosition += header->fTotalLength;
207 TBase* operator->() const { return this->get(); }
215 template<typename TBase, typename TAlign>
216 void GrTRecorder<TBase, TAlign>::reset() {
218 while (iter.next()) {
221 fHeadBlock->fBack = 0;
222 MemBlock::Free(fHeadBlock->fNext);
223 fHeadBlock->fNext = NULL;
224 fTailBlock = fHeadBlock;
228 ////////////////////////////////////////////////////////////////////////////////
230 template<typename TItem> struct GrTRecorderAllocWrapper {
231 GrTRecorderAllocWrapper() : fDataLength(0) {}
233 template <typename TBase, typename TAlign>
234 GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData)
235 : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {}
237 const int fDataLength;
240 template <typename TBase, typename TAlign, typename TItem>
241 void* operator new(size_t size, GrTRecorder<TBase, TAlign>& recorder,
242 const GrTRecorderAllocWrapper<TItem>& wrapper) {
243 SkASSERT(size == sizeof(TItem));
244 return recorder.template alloc_back<TItem>(wrapper.fDataLength);
247 template <typename TBase, typename TAlign, typename TItem>
248 void operator delete(void*, GrTRecorder<TBase, TAlign>&, const GrTRecorderAllocWrapper<TItem>&) {
249 // We only provide an operator delete to work around compiler warnings that can come
250 // up for an unmatched operator new when compiling with exceptions.
254 #define GrNEW_APPEND_TO_RECORDER(recorder, type_name, args) \
255 (new (recorder, GrTRecorderAllocWrapper<type_name>()) type_name args)
257 #define GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, type_name, args, size_of_data) \
258 (new (recorder, GrTRecorderAllocWrapper<type_name>(recorder, size_of_data)) type_name args)