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 GrTBaseList_DEFINED |
| 9 #define GrTBaseList_DEFINED |
| 10 |
| 11 #include "SkTemplates.h" |
| 12 #include "SkTypes.h" |
| 13 |
| 14 template<typename TBase, typename TAlign> class GrTBaseList; |
| 15 template<typename T> class GrTBaseListAllocDesc; |
| 16 |
| 17 template <typename TBase, typename TAlign, typename T> |
| 18 void* operator new(size_t, GrTBaseList<TBase, TAlign>&, const GrTBaseListAllocDe
sc<T>&); |
| 19 |
| 20 /** |
| 21 * List of objects with a common base type and permanent memory addresses. |
| 22 * |
| 23 * This class preallocates its own chunks of memory for storing items in the lis
t, so new |
| 24 * objects can be created without excessive calls to malloc(). |
| 25 * |
| 26 * To create a new item and append it to the back of the list, use the following
macros: |
| 27 * |
| 28 * GrNEW_APPEND_TO_TBASELIST(list, SubclassName, (args)) |
| 29 * GrNEW_APPEND_WITH_DATA_TO_TBASELIST(list, SubclassName, (args), extraByte
sToAllocate) |
| 30 * |
| 31 * @param TBase Common base type of items in the list. If TBase is not a class
with a |
| 32 * virtual destructor, the client is responsible for invoking any
necessary |
| 33 * destructors. |
| 34 * |
| 35 * For now, any subclass used in the list must have the same star
t address |
| 36 * as TBase (or in other words, the types must be convertible via |
| 37 * reinterpret_cast<>). Classes with multiple inheritance (or any
subclass |
| 38 * on an obscure compiler) may not be compatible. This is runtime
asserted |
| 39 * in debug builds. |
| 40 * |
| 41 * @param TAlign A type whose size is the desired memory alignment for object a
llocations. |
| 42 * This should be the largest known alignment requirement for all
objects |
| 43 * that may be stored in the buffer. |
| 44 */ |
| 45 template<typename TBase, typename TAlign> class GrTBaseList : SkNoncopyable { |
| 46 public: |
| 47 class Iter; |
| 48 |
| 49 /** |
| 50 * Create a list for items with a common base type. |
| 51 * |
| 52 * @param initialSizeInBytes The amount of memory reserved by the list init
ially, and |
| 53 * and after calls to reset(). |
| 54 */ |
| 55 GrTBaseList(int initialSizeInBytes) |
| 56 : fHeadBlock(bytes_to_length(initialSizeInBytes)), |
| 57 fTailBlock(&fHeadBlock), |
| 58 fLastItem(NULL) {} |
| 59 |
| 60 ~GrTBaseList() { this->reset(); } |
| 61 |
| 62 bool empty() { return NULL == fLastItem; } |
| 63 |
| 64 TBase& back() { |
| 65 SkASSERT(!this->empty()); |
| 66 return *fLastItem; |
| 67 } |
| 68 |
| 69 /** |
| 70 * Destruct all items in the list and reset to empty. |
| 71 */ |
| 72 void reset(); |
| 73 |
| 74 private: |
| 75 static int bytes_to_length(int bytes) { return (bytes + sizeof(TAlign) - 1)
/ sizeof(TAlign); } |
| 76 |
| 77 struct ItemHeader { |
| 78 int fTotalLength; |
| 79 }; |
| 80 enum { kHeaderLength = (sizeof(ItemHeader) + sizeof(TAlign) - 1) / sizeof(TA
lign) }; |
| 81 |
| 82 template<typename T> void* alloc_back(const GrTBaseListAllocDesc<T>&); |
| 83 |
| 84 struct MemBlock { |
| 85 MemBlock(int length) : fLength(length), fBack(0), fBuffer(fLength) {} |
| 86 const int fLength; |
| 87 int fBack; |
| 88 SkAutoTMalloc<TAlign> fBuffer; |
| 89 SkAutoTDelete<MemBlock> fNext; |
| 90 }; |
| 91 MemBlock fHeadBlock; |
| 92 MemBlock* fTailBlock; |
| 93 |
| 94 TBase* fLastItem; |
| 95 |
| 96 template <typename UBase, typename UAlign, typename U> |
| 97 friend void* operator new(size_t, GrTBaseList<UBase, UAlign>&, const GrTBase
ListAllocDesc<U>&); |
| 98 |
| 99 friend class Iter; |
| 100 }; |
| 101 |
| 102 /** |
| 103 * Describes a new object allocation for GrTBaseList. Captures type and size. |
| 104 */ |
| 105 template<typename T> class GrTBaseListAllocDesc { |
| 106 public: |
| 107 GrTBaseListAllocDesc(int extraBytesToAllocate = 0) |
| 108 : fBytesToAllocate(sizeof(T) + extraBytesToAllocate) { |
| 109 SkASSERT(extraBytesToAllocate >= 0); |
| 110 } |
| 111 int bytesToAllocate() const { return fBytesToAllocate; } |
| 112 private: |
| 113 const int fBytesToAllocate; |
| 114 }; |
| 115 |
| 116 //////////////////////////////////////////////////////////////////////////////// |
| 117 |
| 118 template <typename TBase, typename TAlign, typename T> |
| 119 void* operator new(size_t expectedClassSize, |
| 120 GrTBaseList<TBase, TAlign>& list, |
| 121 const GrTBaseListAllocDesc<T>& desc) { |
| 122 SkASSERT(expectedClassSize == sizeof(T)); |
| 123 return list.alloc_back(desc); |
| 124 } |
| 125 |
| 126 // Skia doesn't use C++ exceptions but it may be compiled with them enabled. Hav
ing an op delete |
| 127 // to match the op new silences warnings about missing op delete when a construc
tor throws an |
| 128 // exception. |
| 129 template <typename TBase, typename TAlign, typename T> |
| 130 void operator delete(void*, GrTBaseList<TBase, TAlign>&, const GrTBaseListAllocD
esc<T>&) { |
| 131 SK_CRASH(); |
| 132 } |
| 133 |
| 134 template<typename TBase, typename TAlign> |
| 135 template<typename T> |
| 136 void* GrTBaseList<TBase, TAlign>::alloc_back(const GrTBaseListAllocDesc<T>& desc
) { |
| 137 SkASSERT(desc.bytesToAllocate() >= (int)sizeof(T)); |
| 138 const int totalLength = kHeaderLength + bytes_to_length(desc.bytesToAllocate
()); |
| 139 |
| 140 if (fTailBlock->fBack + totalLength > fTailBlock->fLength) { |
| 141 SkASSERT(NULL == fTailBlock->fNext.get()); |
| 142 MemBlock* next = SkNEW_ARGS(MemBlock, (SkTMax(2 * fTailBlock->fLength, t
otalLength))); |
| 143 fTailBlock->fNext.reset(next); |
| 144 fTailBlock = next; |
| 145 } |
| 146 |
| 147 ItemHeader* header = reinterpret_cast<ItemHeader*>(&fTailBlock->fBuffer[fTai
lBlock->fBack]); |
| 148 TBase* rawPtr = reinterpret_cast<T*>(&fTailBlock->fBuffer[fTailBlock->fBack
+ kHeaderLength]); |
| 149 fTailBlock->fBack += totalLength; |
| 150 |
| 151 header->fTotalLength = totalLength; |
| 152 |
| 153 fLastItem = rawPtr; |
| 154 |
| 155 // FIXME: We currently require that the base and subclass share the same sta
rt address. |
| 156 // This is not required by the C++ spec, and is likely to not be true in the
case of |
| 157 // multiple inheritance or a base class that doesn't have virtual methods (w
hen the |
| 158 // subclass does). It would be ideal to find a more robust solution that com
es at no |
| 159 // extra cost to performance or code generality. |
| 160 SkDEBUGCODE(void* baseAddr = fLastItem; |
| 161 void* subclassAddr = rawPtr); |
| 162 SkASSERT(baseAddr == subclassAddr); |
| 163 |
| 164 return rawPtr; |
| 165 } |
| 166 |
| 167 template<typename TBase, typename TAlign> |
| 168 class GrTBaseList<TBase, TAlign>::Iter { |
| 169 public: |
| 170 Iter(GrTBaseList& list) : fBlock(&list.fHeadBlock), fPosition(0), fItem(NULL
) {} |
| 171 |
| 172 bool next() { |
| 173 if (fPosition >= fBlock->fBack) { |
| 174 SkASSERT(fPosition == fBlock->fBack); |
| 175 if (NULL == fBlock->fNext.get()) { |
| 176 return false; |
| 177 } |
| 178 SkASSERT(0 != fBlock->fNext->fBack); |
| 179 fBlock = fBlock->fNext.get(); |
| 180 fPosition = 0; |
| 181 } |
| 182 |
| 183 ItemHeader* header = reinterpret_cast<ItemHeader*>(&fBlock->fBuffer[fPos
ition]); |
| 184 fItem = reinterpret_cast<TBase*>(&fBlock->fBuffer[fPosition + kHeaderLen
gth]); |
| 185 fPosition += header->fTotalLength; |
| 186 return true; |
| 187 } |
| 188 |
| 189 TBase* get() const { |
| 190 SkASSERT(fItem); |
| 191 return fItem; |
| 192 } |
| 193 |
| 194 TBase* operator->() const { return this->get(); } |
| 195 |
| 196 private: |
| 197 MemBlock* fBlock; |
| 198 int fPosition; |
| 199 TBase* fItem; |
| 200 }; |
| 201 |
| 202 template<typename TBase, typename TAlign> |
| 203 void GrTBaseList<TBase, TAlign>::reset() { |
| 204 Iter iter(*this); |
| 205 while (iter.next()) { |
| 206 iter->~TBase(); |
| 207 } |
| 208 fHeadBlock.fBack = 0; |
| 209 fHeadBlock.fNext.free(); |
| 210 fTailBlock = &fHeadBlock; |
| 211 fLastItem = NULL; |
| 212 } |
| 213 |
| 214 #define GrNEW_APPEND_TO_TBASELIST(list, type_name, args) \ |
| 215 (new (list, GrTBaseListAllocDesc<type_name>()) type_name args) |
| 216 |
| 217 #define GrNEW_APPEND_WITH_DATA_TO_TBASELIST(list, type_name, args, extra_bytes_t
o_allocate) \ |
| 218 (new (list, GrTBaseListAllocDesc<type_name>(extra_bytes_to_allocate)) type_n
ame args) |
| 219 |
| 220 #endif |
OLD | NEW |