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 * @param TBase Common base type of items in the list. If TBase is not a class with a | |
30 * virtual destructor, the client is responsible for invoking any necessary | |
31 * destructors. | |
32 * | |
33 * For now, any subclass used in the list must have the same star t address | |
34 * as TBase (or in other words, the types must be convertible via | |
35 * reinterpret_cast<>). Classes with multiple inheritance (or any subclass | |
36 * on an obscure compiler) may not be compatible. This is runtime asserted | |
37 * in debug builds. | |
38 * | |
39 * @param TAlign A type whose size is the desired memory alignment for object a llocations. | |
40 * This should be the largest known alignment requirement for all objects | |
41 * that may be stored in the list. | |
42 */ | |
43 template<typename TBase, typename TAlign> class GrTRecorder : SkNoncopyable { | |
44 public: | |
45 class Iter; | |
46 | |
47 /** | |
48 * Create a recorder. | |
49 * | |
50 * @param initialSizeInBytes The amount of memory reserved by the recorder initially, | |
51 and after calls to reset(). | |
52 */ | |
53 GrTRecorder(int initialSizeInBytes) | |
54 : fHeadBlock(LengthOf(initialSizeInBytes)), | |
55 fTailBlock(&fHeadBlock), | |
56 fLastItem(NULL) {} | |
57 | |
58 ~GrTRecorder() { this->reset(); } | |
59 | |
60 bool empty() { return !fLastItem; } | |
61 | |
62 TBase& back() { | |
63 SkASSERT(!this->empty()); | |
64 return *fLastItem; | |
65 } | |
66 | |
67 /** | |
68 * Destruct all items in the list and reset to empty. | |
69 */ | |
70 void reset(); | |
71 | |
72 /** | |
73 * Retrieve the extra data associated with an item that was allocated using | |
74 * GrNEW_APPEND_WITH_DATA_TO_RECORDER(). | |
75 * | |
76 * @param item The item whose data to retrieve. The pointer must be of the same type | |
77 * that was allocated initally; it can't be a pointer to a base class. | |
78 * | |
79 * @return The item's associated data. | |
80 */ | |
81 template<typename TItem> static const void* GetDataForItem(const TItem* item ) { | |
82 const TAlign* ptr = reinterpret_cast<const TAlign*>(item); | |
83 return &ptr[length_of<TItem>::kValue]; | |
84 } | |
85 template<typename TItem> static void* GetDataForItem(TItem* item) { | |
86 TAlign* ptr = reinterpret_cast<TAlign*>(item); | |
87 return &ptr[length_of<TItem>::kValue]; | |
88 } | |
89 | |
90 private: | |
91 template<typename TItem> struct length_of { | |
92 enum { kValue = (sizeof(TItem) + sizeof(TAlign) - 1) / sizeof(TAlign) }; | |
93 }; | |
94 static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeo f(TAlign); } | |
95 | |
96 struct ItemHeader { | |
97 int fTotalLength; | |
98 }; | |
99 enum { kHeaderLength = length_of<ItemHeader>::kValue }; | |
100 | |
101 template<typename TItem> TItem* alloc_back(int dataLength); | |
102 | |
103 struct MemBlock { | |
104 MemBlock(int length) : fLength(length), fBack(0), fBuffer(fLength) {} | |
bsalomon
2014/10/11 11:32:49
I didn't pay that much attention to MemBlock befor
Chris Dalton
2014/10/13 18:27:46
It was just a quick code complexity vs perf impact
| |
105 const int fLength; | |
106 int fBack; | |
107 SkAutoTMalloc<TAlign> fBuffer; | |
108 SkAutoTDelete<MemBlock> fNext; | |
109 }; | |
110 MemBlock fHeadBlock; | |
111 MemBlock* fTailBlock; | |
112 | |
113 TBase* fLastItem; | |
114 | |
115 template<typename TItem> friend struct GrTRecorderAllocWrapper; | |
116 | |
117 template <typename UBase, typename UAlign, typename UAlloc> | |
118 friend void* operator new(size_t, GrTRecorder<UBase, UAlign>&, | |
119 const GrTRecorderAllocWrapper<UAlloc>&); | |
120 | |
121 friend class Iter; | |
122 }; | |
123 | |
124 //////////////////////////////////////////////////////////////////////////////// | |
125 | |
126 template<typename TBase, typename TAlign> | |
127 template<typename TItem> | |
128 TItem* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) { | |
129 const int totalLength = kHeaderLength + length_of<TItem>::kValue + dataLengt h; | |
130 | |
131 if (fTailBlock->fBack + totalLength > fTailBlock->fLength) { | |
132 SkASSERT(!fTailBlock->fNext.get()); | |
133 MemBlock* next = SkNEW_ARGS(MemBlock, (SkTMax(2 * fTailBlock->fLength, t otalLength))); | |
134 fTailBlock->fNext.reset(next); | |
135 fTailBlock = next; | |
136 } | |
137 | |
138 ItemHeader* header = reinterpret_cast<ItemHeader*>(&fTailBlock->fBuffer[fTai lBlock->fBack]); | |
139 TItem* rawPtr = | |
140 reinterpret_cast<TItem*>(&fTailBlock->fBuffer[fTailBlock->fBack + kHeade rLength]); | |
141 | |
142 header->fTotalLength = totalLength; | |
143 fLastItem = rawPtr; | |
144 fTailBlock->fBack += totalLength; | |
145 | |
146 // FIXME: We currently require that the base and subclass share the same sta rt address. | |
147 // This is not required by the C++ spec, and is likely to not be true in the case of | |
148 // multiple inheritance or a base class that doesn't have virtual methods (w hen the | |
149 // subclass does). It would be ideal to find a more robust solution that com es at no | |
150 // extra cost to performance or code generality. | |
151 SkDEBUGCODE(void* baseAddr = fLastItem; | |
152 void* subclassAddr = rawPtr); | |
153 SkASSERT(baseAddr == subclassAddr); | |
154 | |
155 return rawPtr; | |
156 } | |
157 | |
158 template<typename TBase, typename TAlign> | |
159 class GrTRecorder<TBase, TAlign>::Iter { | |
160 public: | |
161 Iter(GrTRecorder& recorder) : fBlock(&recorder.fHeadBlock), fPosition(0), fI tem(NULL) {} | |
162 | |
163 bool next() { | |
164 if (fPosition >= fBlock->fBack) { | |
165 SkASSERT(fPosition == fBlock->fBack); | |
166 if (!fBlock->fNext.get()) { | |
167 return false; | |
168 } | |
169 SkASSERT(0 != fBlock->fNext->fBack); | |
170 fBlock = fBlock->fNext.get(); | |
171 fPosition = 0; | |
172 } | |
173 | |
174 ItemHeader* header = reinterpret_cast<ItemHeader*>(&fBlock->fBuffer[fPos ition]); | |
175 fItem = reinterpret_cast<TBase*>(&fBlock->fBuffer[fPosition + kHeaderLen gth]); | |
176 fPosition += header->fTotalLength; | |
177 return true; | |
178 } | |
179 | |
180 TBase* get() const { | |
181 SkASSERT(fItem); | |
182 return fItem; | |
183 } | |
184 | |
185 TBase* operator->() const { return this->get(); } | |
186 | |
187 private: | |
188 MemBlock* fBlock; | |
189 int fPosition; | |
190 TBase* fItem; | |
191 }; | |
192 | |
193 template<typename TBase, typename TAlign> | |
194 void GrTRecorder<TBase, TAlign>::reset() { | |
195 Iter iter(*this); | |
196 while (iter.next()) { | |
197 iter->~TBase(); | |
198 } | |
199 fHeadBlock.fBack = 0; | |
200 fHeadBlock.fNext.free(); | |
201 fTailBlock = &fHeadBlock; | |
202 fLastItem = NULL; | |
203 } | |
204 | |
205 //////////////////////////////////////////////////////////////////////////////// | |
206 | |
207 template<typename TItem> struct GrTRecorderAllocWrapper { | |
208 GrTRecorderAllocWrapper() : fDataLength(0) {} | |
209 | |
210 template <typename TBase, typename TAlign> | |
211 GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData) | |
212 : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {} | |
213 | |
214 const int fDataLength; | |
215 }; | |
216 | |
217 template <typename TBase, typename TAlign, typename TItem> | |
218 void* operator new(size_t size, GrTRecorder<TBase, TAlign>& recorder, | |
219 const GrTRecorderAllocWrapper<TItem>& wrapper) { | |
220 SkASSERT(size == sizeof(TItem)); | |
221 return recorder.template alloc_back<TItem>(wrapper.fDataLength); | |
222 } | |
223 | |
224 template <typename TBase, typename TAlign, typename TItem> | |
225 void operator delete(void*, GrTRecorder<TBase, TAlign>&, const GrTRecorderAllocW rapper<TItem>&) { | |
226 // We only provide an operator delete to work around compiler warnings that can come | |
227 // up for an unmatched operator new when compiling with exceptions. | |
228 SK_CRASH(); | |
229 } | |
230 | |
231 #define GrNEW_APPEND_TO_RECORDER(recorder, type_name, args) \ | |
232 (new (recorder, GrTRecorderAllocWrapper<type_name>()) type_name args) | |
233 | |
234 #define GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, type_name, args, size_of_da ta) \ | |
235 (new (recorder, GrTRecorderAllocWrapper<type_name>(recorder, size_of_data)) type_name args) | |
236 | |
237 #endif | |
OLD | NEW |