OLD | NEW |
| (Empty) |
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 "SkTemplates.h" | |
12 #include "SkTypes.h" | |
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, an
d | |
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), sizeOf
Data) | |
28 * | |
29 * Upon reset or delete, the items are destructed in the same order they were re
ceived, | |
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 star
t 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 a
llocations. | |
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 | |
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))), | |
58 fTailBlock(fHeadBlock), | |
59 fLastItem(NULL) {} | |
60 | |
61 ~GrTRecorder() { | |
62 this->reset(); | |
63 sk_free(fHeadBlock); | |
64 } | |
65 | |
66 bool empty() { return !fLastItem; } | |
67 | |
68 TBase& back() { | |
69 SkASSERT(!this->empty()); | |
70 return *fLastItem; | |
71 } | |
72 | |
73 /** | |
74 * Destruct all items in the list and reset to empty. | |
75 */ | |
76 void reset(); | |
77 | |
78 /** | |
79 * Retrieve the extra data associated with an item that was allocated using | |
80 * GrNEW_APPEND_WITH_DATA_TO_RECORDER(). | |
81 * | |
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. | |
84 * | |
85 * @return The item's associated data. | |
86 */ | |
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]; | |
90 } | |
91 template<typename TItem> static void* GetDataForItem(TItem* item) { | |
92 TAlign* ptr = reinterpret_cast<TAlign*>(item); | |
93 return &ptr[length_of<TItem>::kValue]; | |
94 } | |
95 | |
96 private: | |
97 template<typename TItem> struct length_of { | |
98 enum { kValue = (sizeof(TItem) + sizeof(TAlign) - 1) / sizeof(TAlign) }; | |
99 }; | |
100 static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeo
f(TAlign); } | |
101 | |
102 struct Header { | |
103 int fTotalLength; | |
104 }; | |
105 template<typename TItem> TItem* alloc_back(int dataLength); | |
106 | |
107 struct MemBlock { | |
108 static MemBlock* Alloc(int length) { | |
109 void* ptr = sk_malloc_throw(sizeof(TAlign) * (length_of<MemBlock>::k
Value + length)); | |
110 return SkNEW_PLACEMENT_ARGS(ptr, MemBlock, (length)); | |
111 } | |
112 TAlign& operator [](int i) { | |
113 return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue +
i]; | |
114 } | |
115 ~MemBlock() { sk_free(fNext); } | |
116 | |
117 const int fLength; | |
118 int fBack; | |
119 MemBlock* fNext; | |
120 | |
121 private: | |
122 MemBlock(int length) : fLength(length), fBack(0), fNext(NULL) {} | |
123 }; | |
124 MemBlock* const fHeadBlock; | |
125 MemBlock* fTailBlock; | |
126 | |
127 TBase* fLastItem; | |
128 | |
129 template<typename TItem> friend struct GrTRecorderAllocWrapper; | |
130 | |
131 template <typename UBase, typename UAlign, typename UAlloc> | |
132 friend void* operator new(size_t, GrTRecorder<UBase, UAlign>&, | |
133 const GrTRecorderAllocWrapper<UAlloc>&); | |
134 | |
135 friend class Iter; | |
136 }; | |
137 | |
138 //////////////////////////////////////////////////////////////////////////////// | |
139 | |
140 template<typename TBase, typename TAlign> | |
141 template<typename TItem> | |
142 TItem* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) { | |
143 const int totalLength = length_of<Header>::kValue + length_of<TItem>::kValue
+ dataLength; | |
144 | |
145 if (fTailBlock->fBack + totalLength > fTailBlock->fLength) { | |
146 SkASSERT(!fTailBlock->fNext); | |
147 fTailBlock->fNext = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, tota
lLength)); | |
148 fTailBlock = fTailBlock->fNext; | |
149 } | |
150 | |
151 Header* header = reinterpret_cast<Header*>(&(*fTailBlock)[fTailBlock->fBack]
); | |
152 TItem* rawPtr = reinterpret_cast<TItem*>( | |
153 &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kV
alue]); | |
154 | |
155 header->fTotalLength = totalLength; | |
156 fLastItem = rawPtr; | |
157 fTailBlock->fBack += totalLength; | |
158 | |
159 // FIXME: We currently require that the base and subclass share the same sta
rt address. | |
160 // This is not required by the C++ spec, and is likely to not be true in the
case of | |
161 // multiple inheritance or a base class that doesn't have virtual methods (w
hen the | |
162 // subclass does). It would be ideal to find a more robust solution that com
es at no | |
163 // extra cost to performance or code generality. | |
164 SkDEBUGCODE(void* baseAddr = fLastItem; | |
165 void* subclassAddr = rawPtr); | |
166 SkASSERT(baseAddr == subclassAddr); | |
167 | |
168 return rawPtr; | |
169 } | |
170 | |
171 template<typename TBase, typename TAlign> | |
172 class GrTRecorder<TBase, TAlign>::Iter { | |
173 public: | |
174 Iter(GrTRecorder& recorder) : fBlock(recorder.fHeadBlock), fPosition(0), fIt
em(NULL) {} | |
175 | |
176 bool next() { | |
177 if (fPosition >= fBlock->fBack) { | |
178 SkASSERT(fPosition == fBlock->fBack); | |
179 if (!fBlock->fNext) { | |
180 return false; | |
181 } | |
182 SkASSERT(0 != fBlock->fNext->fBack); | |
183 fBlock = fBlock->fNext; | |
184 fPosition = 0; | |
185 } | |
186 | |
187 Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]); | |
188 fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header
>::kValue]); | |
189 fPosition += header->fTotalLength; | |
190 return true; | |
191 } | |
192 | |
193 TBase* get() const { | |
194 SkASSERT(fItem); | |
195 return fItem; | |
196 } | |
197 | |
198 TBase* operator->() const { return this->get(); } | |
199 | |
200 private: | |
201 MemBlock* fBlock; | |
202 int fPosition; | |
203 TBase* fItem; | |
204 }; | |
205 | |
206 template<typename TBase, typename TAlign> | |
207 void GrTRecorder<TBase, TAlign>::reset() { | |
208 Iter iter(*this); | |
209 while (iter.next()) { | |
210 iter->~TBase(); | |
211 } | |
212 fHeadBlock->fBack = 0; | |
213 sk_free(fHeadBlock->fNext); | |
214 fHeadBlock->fNext = NULL; | |
215 fTailBlock = fHeadBlock; | |
216 fLastItem = NULL; | |
217 } | |
218 | |
219 //////////////////////////////////////////////////////////////////////////////// | |
220 | |
221 template<typename TItem> struct GrTRecorderAllocWrapper { | |
222 GrTRecorderAllocWrapper() : fDataLength(0) {} | |
223 | |
224 template <typename TBase, typename TAlign> | |
225 GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData) | |
226 : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {} | |
227 | |
228 const int fDataLength; | |
229 }; | |
230 | |
231 template <typename TBase, typename TAlign, typename TItem> | |
232 void* operator new(size_t size, GrTRecorder<TBase, TAlign>& recorder, | |
233 const GrTRecorderAllocWrapper<TItem>& wrapper) { | |
234 SkASSERT(size == sizeof(TItem)); | |
235 return recorder.template alloc_back<TItem>(wrapper.fDataLength); | |
236 } | |
237 | |
238 template <typename TBase, typename TAlign, typename TItem> | |
239 void operator delete(void*, GrTRecorder<TBase, TAlign>&, const GrTRecorderAllocW
rapper<TItem>&) { | |
240 // We only provide an operator delete to work around compiler warnings that
can come | |
241 // up for an unmatched operator new when compiling with exceptions. | |
242 SK_CRASH(); | |
243 } | |
244 | |
245 #define GrNEW_APPEND_TO_RECORDER(recorder, type_name, args) \ | |
246 (new (recorder, GrTRecorderAllocWrapper<type_name>()) type_name args) | |
247 | |
248 #define GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, type_name, args, size_of_da
ta) \ | |
249 (new (recorder, GrTRecorderAllocWrapper<type_name>(recorder, size_of_data))
type_name args) | |
250 | |
251 #endif | |
OLD | NEW |