blob: 74dd1380b0008281cec2d74787ecc28254515370 [file] [log] [blame]
cdalton6819df32014-10-15 13:43:48 -07001/*
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
cdalton6819df32014-10-15 13:43:48 -070011#include "SkTypes.h"
Mike Klein79aea6a2018-06-11 10:45:26 -040012#include <new>
cdalton6819df32014-10-15 13:43:48 -070013
14template<typename TBase, typename TAlign> class GrTRecorder;
15template<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 */
46template<typename TBase, typename TAlign> class GrTRecorder : SkNoncopyable {
47public:
48 class Iter;
cdalton72badbd2015-04-16 10:42:49 -070049 class ReverseIter;
cdalton6819df32014-10-15 13:43:48 -070050
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 */
57 GrTRecorder(int initialSizeInBytes)
halcanary96fcdcc2015-08-27 07:41:13 -070058 : fHeadBlock(MemBlock::Alloc(LengthOf(initialSizeInBytes), nullptr)),
cdalton6819df32014-10-15 13:43:48 -070059 fTailBlock(fHeadBlock),
halcanary96fcdcc2015-08-27 07:41:13 -070060 fLastItem(nullptr) {}
cdalton6819df32014-10-15 13:43:48 -070061
62 ~GrTRecorder() {
63 this->reset();
64 MemBlock::Free(fHeadBlock);
65 }
66
67 bool empty() { return !fLastItem; }
68
69 TBase& back() {
70 SkASSERT(!this->empty());
bsalomon2aad5f12015-07-30 15:57:33 -070071 return *reinterpret_cast<TBase*>(fLastItem);
cdalton6819df32014-10-15 13:43:48 -070072 }
73
74 /**
bsalomon77d77f42014-11-21 14:38:06 -080075 * 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 /**
cdalton6819df32014-10-15 13:43:48 -070081 * 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 */
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 }
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
103private:
104 template<typename TItem> struct length_of {
105 enum { kValue = (sizeof(TItem) + sizeof(TAlign) - 1) / sizeof(TAlign) };
106 };
107 static int LengthOf(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); }
108
109 struct Header {
bsalomon77d77f42014-11-21 14:38:06 -0800110 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.
cdalton6819df32014-10-15 13:43:48 -0700112 };
bsalomon2aad5f12015-07-30 15:57:33 -0700113 template<typename TItem> void* alloc_back(int dataLength);
cdalton6819df32014-10-15 13:43:48 -0700114
115 struct MemBlock : SkNoncopyable {
halcanary96fcdcc2015-08-27 07:41:13 -0700116 /** Allocates a new block and appends it to prev if not nullptr. The length param is in units
bsalomon77d77f42014-11-21 14:38:06 -0800117 of TAlign. */
118 static MemBlock* Alloc(int length, MemBlock* prev) {
cdalton6819df32014-10-15 13:43:48 -0700119 MemBlock* block = reinterpret_cast<MemBlock*>(
120 sk_malloc_throw(sizeof(TAlign) * (length_of<MemBlock>::kValue + length)));
121 block->fLength = length;
122 block->fBack = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700123 block->fNext = nullptr;
bsalomon77d77f42014-11-21 14:38:06 -0800124 block->fPrev = prev;
125 if (prev) {
halcanary96fcdcc2015-08-27 07:41:13 -0700126 SkASSERT(nullptr == prev->fNext);
bsalomon77d77f42014-11-21 14:38:06 -0800127 prev->fNext = block;
128 }
cdalton6819df32014-10-15 13:43:48 -0700129 return block;
130 }
131
bsalomon77d77f42014-11-21 14:38:06 -0800132 // Frees from this block forward. Also adjusts prev block's next ptr.
cdalton6819df32014-10-15 13:43:48 -0700133 static void Free(MemBlock* block) {
bsalomon77d77f42014-11-21 14:38:06 -0800134 if (block && block->fPrev) {
135 SkASSERT(block->fPrev->fNext == block);
halcanary96fcdcc2015-08-27 07:41:13 -0700136 block->fPrev->fNext = nullptr;
cdalton6819df32014-10-15 13:43:48 -0700137 }
bsalomon77d77f42014-11-21 14:38:06 -0800138 while (block) {
139 MemBlock* next = block->fNext;
140 sk_free(block);
141 block = next;
142 }
cdalton6819df32014-10-15 13:43:48 -0700143 }
144
145 TAlign& operator [](int i) {
146 return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue + i];
147 }
148
bsalomon77d77f42014-11-21 14:38:06 -0800149 int fLength; // Length in units of TAlign of the block.
150 int fBack; // Offset, in TAligns, to unused portion of the memory block.
cdalton6819df32014-10-15 13:43:48 -0700151 MemBlock* fNext;
bsalomon77d77f42014-11-21 14:38:06 -0800152 MemBlock* fPrev;
cdalton6819df32014-10-15 13:43:48 -0700153 };
154 MemBlock* const fHeadBlock;
155 MemBlock* fTailBlock;
156
bsalomon2aad5f12015-07-30 15:57:33 -0700157 void* fLastItem; // really a ptr to TBase
cdalton6819df32014-10-15 13:43:48 -0700158
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;
cdalton72badbd2015-04-16 10:42:49 -0700166 friend class ReverseIter;
cdalton6819df32014-10-15 13:43:48 -0700167};
168
169////////////////////////////////////////////////////////////////////////////////
170
171template<typename TBase, typename TAlign>
bsalomon77d77f42014-11-21 14:38:06 -0800172void 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;
bsalomon2aad5f12015-07-30 15:57:33 -0700177 reinterpret_cast<TBase*>(fLastItem)->~TBase();
bsalomon77d77f42014-11-21 14:38:06 -0800178
179 int lastItemLength = header->fPrevLength;
180
181 if (!header->fPrevLength) {
182 // We popped the first entry in the recorder.
183 SkASSERT(0 == fTailBlock->fBack);
halcanary96fcdcc2015-08-27 07:41:13 -0700184 fLastItem = nullptr;
bsalomon77d77f42014-11-21 14:38:06 -0800185 return;
186 }
cdalton72badbd2015-04-16 10:42:49 -0700187 while (!fTailBlock->fBack) {
bsalomon77d77f42014-11-21 14:38:06 -0800188 // 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 }
bsalomon2aad5f12015-07-30 15:57:33 -0700193 fLastItem = &(*fTailBlock)[fTailBlock->fBack - lastItemLength + length_of<Header>::kValue];
bsalomon77d77f42014-11-21 14:38:06 -0800194}
195
196template<typename TBase, typename TAlign>
cdalton6819df32014-10-15 13:43:48 -0700197template<typename TItem>
bsalomon2aad5f12015-07-30 15:57:33 -0700198void* GrTRecorder<TBase, TAlign>::alloc_back(int dataLength) {
bsalomon77d77f42014-11-21 14:38:06 -0800199 // 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
cdalton6819df32014-10-15 13:43:48 -0700208 const int totalLength = length_of<Header>::kValue + length_of<TItem>::kValue + dataLength;
209
bsalomon77d77f42014-11-21 14:38:06 -0800210 // 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.
cdaltonc4650ee2014-11-07 12:51:18 -0800213 while (fTailBlock->fBack + totalLength > fTailBlock->fLength) {
214 if (!fTailBlock->fNext) {
bsalomon77d77f42014-11-21 14:38:06 -0800215 fTailBlock = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, totalLength), fTailBlock);
216 } else {
217 fTailBlock = fTailBlock->fNext;
cdaltonc4650ee2014-11-07 12:51:18 -0800218 }
cdaltonc4650ee2014-11-07 12:51:18 -0800219 SkASSERT(0 == fTailBlock->fBack);
cdalton6819df32014-10-15 13:43:48 -0700220 }
221
222 Header* header = reinterpret_cast<Header*>(&(*fTailBlock)[fTailBlock->fBack]);
bsalomon2aad5f12015-07-30 15:57:33 -0700223 void* rawPtr = &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kValue];
cdalton6819df32014-10-15 13:43:48 -0700224
225 header->fTotalLength = totalLength;
bsalomon77d77f42014-11-21 14:38:06 -0800226 header->fPrevLength = prevLength;
cdalton6819df32014-10-15 13:43:48 -0700227 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
cdalton72badbd2015-04-16 10:42:49 -0700242/**
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 */
cdalton6819df32014-10-15 13:43:48 -0700251template<typename TBase, typename TAlign>
252class GrTRecorder<TBase, TAlign>::Iter {
253public:
halcanary96fcdcc2015-08-27 07:41:13 -0700254 Iter(GrTRecorder& recorder) : fBlock(recorder.fHeadBlock), fPosition(0), fItem(nullptr) {}
cdalton6819df32014-10-15 13:43:48 -0700255
256 bool next() {
cdaltonc4650ee2014-11-07 12:51:18 -0800257 while (fPosition >= fBlock->fBack) {
cdalton6819df32014-10-15 13:43:48 -0700258 SkASSERT(fPosition == fBlock->fBack);
259 if (!fBlock->fNext) {
260 return false;
261 }
cdalton6819df32014-10-15 13:43:48 -0700262 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
272 TBase* get() const {
273 SkASSERT(fItem);
274 return fItem;
275 }
276
277 TBase* operator->() const { return this->get(); }
278
279private:
280 MemBlock* fBlock;
281 int fPosition;
282 TBase* fItem;
283};
284
cdalton72badbd2015-04-16 10:42:49 -0700285/**
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 */
295template<typename TBase, typename TAlign>
296class GrTRecorder<TBase, TAlign>::ReverseIter {
297public:
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
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
325 TBase* get() const { return fItem; }
326 TBase* operator->() const { return this->get(); }
327
328private:
329 MemBlock* fBlock;
330 int fPosition;
331 TBase* fItem;
332};
333
cdalton6819df32014-10-15 13:43:48 -0700334template<typename TBase, typename TAlign>
335void GrTRecorder<TBase, TAlign>::reset() {
336 Iter iter(*this);
337 while (iter.next()) {
338 iter->~TBase();
339 }
cdaltonc4650ee2014-11-07 12:51:18 -0800340
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);
cdaltonc4650ee2014-11-07 12:51:18 -0800346 } else if (fTailBlock->fNext) {
347 MemBlock::Free(fTailBlock->fNext->fNext);
halcanary96fcdcc2015-08-27 07:41:13 -0700348 fTailBlock->fNext->fNext = nullptr;
cdaltonc4650ee2014-11-07 12:51:18 -0800349 }
350
351 for (MemBlock* block = fHeadBlock; block; block = block->fNext) {
352 block->fBack = 0;
353 }
354
cdalton6819df32014-10-15 13:43:48 -0700355 fTailBlock = fHeadBlock;
halcanary96fcdcc2015-08-27 07:41:13 -0700356 fLastItem = nullptr;
cdalton6819df32014-10-15 13:43:48 -0700357}
358
359////////////////////////////////////////////////////////////////////////////////
360
361template<typename TItem> struct GrTRecorderAllocWrapper {
362 GrTRecorderAllocWrapper() : fDataLength(0) {}
363
364 template <typename TBase, typename TAlign>
365 GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData)
366 : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {}
367
368 const int fDataLength;
369};
370
371template <typename TBase, typename TAlign, typename TItem>
372void* 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
378template <typename TBase, typename TAlign, typename TItem>
379void 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.
djsollenf2b340f2016-01-29 08:51:04 -0800382 SK_ABORT("Invalid Operation");
cdalton6819df32014-10-15 13:43:48 -0700383}
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