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 MemBlock::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 : 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; |
| 112 block->fBack = 0; |
| 113 block->fNext = NULL; |
| 114 return block; |
| 115 } |
| 116 |
| 117 static void Free(MemBlock* block) { |
| 118 if (!block) { |
| 119 return; |
| 120 } |
| 121 Free(block->fNext); |
| 122 sk_free(block); |
| 123 } |
| 124 |
| 125 TAlign& operator [](int i) { |
| 126 return reinterpret_cast<TAlign*>(this)[length_of<MemBlock>::kValue +
i]; |
| 127 } |
| 128 |
| 129 int fLength; |
| 130 int fBack; |
| 131 MemBlock* fNext; |
| 132 }; |
| 133 MemBlock* const fHeadBlock; |
| 134 MemBlock* fTailBlock; |
| 135 |
| 136 TBase* fLastItem; |
| 137 |
| 138 template<typename TItem> friend struct GrTRecorderAllocWrapper; |
| 139 |
| 140 template <typename UBase, typename UAlign, typename UItem> |
| 141 friend void* operator new(size_t, GrTRecorder<UBase, UAlign>&, |
| 142 const GrTRecorderAllocWrapper<UItem>&); |
| 143 |
| 144 friend class Iter; |
| 145 }; |
| 146 |
| 147 //////////////////////////////////////////////////////////////////////////////// |
| 148 |
| 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; |
| 153 |
| 154 if (fTailBlock->fBack + totalLength > fTailBlock->fLength) { |
| 155 SkASSERT(!fTailBlock->fNext); |
| 156 fTailBlock->fNext = MemBlock::Alloc(SkTMax(2 * fTailBlock->fLength, tota
lLength)); |
| 157 fTailBlock = fTailBlock->fNext; |
| 158 } |
| 159 |
| 160 Header* header = reinterpret_cast<Header*>(&(*fTailBlock)[fTailBlock->fBack]
); |
| 161 TItem* rawPtr = reinterpret_cast<TItem*>( |
| 162 &(*fTailBlock)[fTailBlock->fBack + length_of<Header>::kV
alue]); |
| 163 |
| 164 header->fTotalLength = totalLength; |
| 165 fLastItem = rawPtr; |
| 166 fTailBlock->fBack += totalLength; |
| 167 |
| 168 // FIXME: We currently require that the base and subclass share the same sta
rt 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 (w
hen the |
| 171 // subclass does). It would be ideal to find a more robust solution that com
es at no |
| 172 // extra cost to performance or code generality. |
| 173 SkDEBUGCODE(void* baseAddr = fLastItem; |
| 174 void* subclassAddr = rawPtr); |
| 175 SkASSERT(baseAddr == subclassAddr); |
| 176 |
| 177 return rawPtr; |
| 178 } |
| 179 |
| 180 template<typename TBase, typename TAlign> |
| 181 class GrTRecorder<TBase, TAlign>::Iter { |
| 182 public: |
| 183 Iter(GrTRecorder& recorder) : fBlock(recorder.fHeadBlock), fPosition(0), fIt
em(NULL) {} |
| 184 |
| 185 bool next() { |
| 186 if (fPosition >= fBlock->fBack) { |
| 187 SkASSERT(fPosition == fBlock->fBack); |
| 188 if (!fBlock->fNext) { |
| 189 return false; |
| 190 } |
| 191 SkASSERT(0 != fBlock->fNext->fBack); |
| 192 fBlock = fBlock->fNext; |
| 193 fPosition = 0; |
| 194 } |
| 195 |
| 196 Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]); |
| 197 fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header
>::kValue]); |
| 198 fPosition += header->fTotalLength; |
| 199 return true; |
| 200 } |
| 201 |
| 202 TBase* get() const { |
| 203 SkASSERT(fItem); |
| 204 return fItem; |
| 205 } |
| 206 |
| 207 TBase* operator->() const { return this->get(); } |
| 208 |
| 209 private: |
| 210 MemBlock* fBlock; |
| 211 int fPosition; |
| 212 TBase* fItem; |
| 213 }; |
| 214 |
| 215 template<typename TBase, typename TAlign> |
| 216 void GrTRecorder<TBase, TAlign>::reset() { |
| 217 Iter iter(*this); |
| 218 while (iter.next()) { |
| 219 iter->~TBase(); |
| 220 } |
| 221 fHeadBlock->fBack = 0; |
| 222 MemBlock::Free(fHeadBlock->fNext); |
| 223 fHeadBlock->fNext = NULL; |
| 224 fTailBlock = fHeadBlock; |
| 225 fLastItem = NULL; |
| 226 } |
| 227 |
| 228 //////////////////////////////////////////////////////////////////////////////// |
| 229 |
| 230 template<typename TItem> struct GrTRecorderAllocWrapper { |
| 231 GrTRecorderAllocWrapper() : fDataLength(0) {} |
| 232 |
| 233 template <typename TBase, typename TAlign> |
| 234 GrTRecorderAllocWrapper(const GrTRecorder<TBase, TAlign>&, int sizeOfData) |
| 235 : fDataLength(GrTRecorder<TBase, TAlign>::LengthOf(sizeOfData)) {} |
| 236 |
| 237 const int fDataLength; |
| 238 }; |
| 239 |
| 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); |
| 245 } |
| 246 |
| 247 template <typename TBase, typename TAlign, typename TItem> |
| 248 void operator delete(void*, GrTRecorder<TBase, TAlign>&, const GrTRecorderAllocW
rapper<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. |
| 251 SK_CRASH(); |
| 252 } |
| 253 |
| 254 #define GrNEW_APPEND_TO_RECORDER(recorder, type_name, args) \ |
| 255 (new (recorder, GrTRecorderAllocWrapper<type_name>()) type_name args) |
| 256 |
| 257 #define GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, type_name, args, size_of_da
ta) \ |
| 258 (new (recorder, GrTRecorderAllocWrapper<type_name>(recorder, size_of_data))
type_name args) |
| 259 |
| 260 #endif |
OLD | NEW |