1 /* 2 * Copyright 2010 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 GrAllocator_DEFINED 9 #define GrAllocator_DEFINED 10 11 #include "GrConfig.h" 12 #include "GrTypes.h" 13 #include "SkTArray.h" 14 #include "SkTypes.h" 15 16 class GrAllocator : SkNoncopyable { 17 public: 18 ~GrAllocator() { this->reset(); } 19 20 /** 21 * Create an allocator 22 * 23 * @param itemSize the size of each item to allocate 24 * @param itemsPerBlock the number of items to allocate at once 25 * @param initialBlock optional memory to use for the first block. 26 * Must be at least itemSize*itemsPerBlock sized. 27 * Caller is responsible for freeing this memory. 28 */ 29 GrAllocator(size_t itemSize, int itemsPerBlock, void* initialBlock) 30 : fItemSize(itemSize) 31 , fItemsPerBlock(itemsPerBlock) 32 , fOwnFirstBlock(nullptr == initialBlock) 33 , fCount(0) 34 , fInsertionIndexInBlock(0) { 35 SkASSERT(itemsPerBlock > 0); 36 fBlockSize = fItemSize * fItemsPerBlock; 37 if (fOwnFirstBlock) { 38 // This force us to allocate a new block on push_back(). 39 fInsertionIndexInBlock = fItemsPerBlock; 40 } else { 41 fBlocks.push_back() = initialBlock; 42 fInsertionIndexInBlock = 0; 43 } 44 } 45 46 /** 47 * Adds an item and returns pointer to it. 48 * 49 * @return pointer to the added item. 50 */ 51 void* push_back() { 52 // we always have at least one block 53 if (fItemsPerBlock == fInsertionIndexInBlock) { 54 fBlocks.push_back() = sk_malloc_throw(fBlockSize); 55 fInsertionIndexInBlock = 0; 56 } 57 void* ret = (char*)fBlocks.back() + fItemSize * fInsertionIndexInBlock; 58 ++fCount; 59 ++fInsertionIndexInBlock; 60 return ret; 61 } 62 63 /** 64 * Remove the last item, only call if count() != 0 65 */ 66 void pop_back() { 67 SkASSERT(fCount); 68 SkASSERT(fInsertionIndexInBlock > 0); 69 --fInsertionIndexInBlock; 70 --fCount; 71 if (0 == fInsertionIndexInBlock) { 72 // Never delete the first block 73 if (fBlocks.count() > 1) { 74 sk_free(fBlocks.back()); 75 fBlocks.pop_back(); 76 fInsertionIndexInBlock = fItemsPerBlock; 77 } 78 } 79 } 80 81 /** 82 * Removes all added items. 83 */ 84 void reset() { 85 int firstBlockToFree = fOwnFirstBlock ? 0 : 1; 86 for (int i = firstBlockToFree; i < fBlocks.count(); ++i) { 87 sk_free(fBlocks[i]); 88 } 89 if (fOwnFirstBlock) { 90 fBlocks.reset(); 91 // This force us to allocate a new block on push_back(). 92 fInsertionIndexInBlock = fItemsPerBlock; 93 } else { 94 fBlocks.pop_back_n(fBlocks.count() - 1); 95 fInsertionIndexInBlock = 0; 96 } 97 fCount = 0; 98 } 99 100 /** 101 * Returns the item count. 102 */ 103 int count() const { 104 return fCount; 105 } 106 107 /** 108 * Is the count 0? 109 */ 110 bool empty() const { return 0 == fCount; } 111 112 /** 113 * Access last item, only call if count() != 0 114 */ 115 void* back() { 116 SkASSERT(fCount); 117 SkASSERT(fInsertionIndexInBlock > 0); 118 return (char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize; 119 } 120 121 /** 122 * Access last item, only call if count() != 0 123 */ 124 const void* back() const { 125 SkASSERT(fCount); 126 SkASSERT(fInsertionIndexInBlock > 0); 127 return (const char*)(fBlocks.back()) + (fInsertionIndexInBlock - 1) * fItemSize; 128 } 129 130 /** 131 * Iterates through the allocator. This is faster than using operator[] when walking linearly 132 * through the allocator. 133 */ 134 class Iter { 135 public: 136 /** 137 * Initializes the iterator. next() must be called before get(). 138 */ 139 Iter(const GrAllocator* allocator) 140 : fAllocator(allocator) 141 , fBlockIndex(-1) 142 , fIndexInBlock(allocator->fItemsPerBlock - 1) 143 , fItemIndex(-1) {} 144 145 /** 146 * Advances the iterator. Iteration is finished when next() returns false. 147 */ 148 bool next() { 149 ++fIndexInBlock; 150 ++fItemIndex; 151 if (fIndexInBlock == fAllocator->fItemsPerBlock) { 152 ++fBlockIndex; 153 fIndexInBlock = 0; 154 } 155 return fItemIndex < fAllocator->fCount; 156 } 157 158 /** 159 * Gets the current iterator value. Call next() at least once before calling. Don't call 160 * after next() returns false. 161 */ 162 void* get() const { 163 SkASSERT(fItemIndex >= 0 && fItemIndex < fAllocator->fCount); 164 return (char*) fAllocator->fBlocks[fBlockIndex] + fIndexInBlock * fAllocator->fItemSize; 165 } 166 167 private: 168 const GrAllocator* fAllocator; 169 int fBlockIndex; 170 int fIndexInBlock; 171 int fItemIndex; 172 }; 173 174 /** 175 * Access item by index. 176 */ 177 void* operator[] (int i) { 178 SkASSERT(i >= 0 && i < fCount); 179 return (char*)fBlocks[i / fItemsPerBlock] + 180 fItemSize * (i % fItemsPerBlock); 181 } 182 183 /** 184 * Access item by index. 185 */ 186 const void* operator[] (int i) const { 187 SkASSERT(i >= 0 && i < fCount); 188 return (const char*)fBlocks[i / fItemsPerBlock] + 189 fItemSize * (i % fItemsPerBlock); 190 } 191 192 protected: 193 /** 194 * Set first block of memory to write into. Must be called before any other methods. 195 * This requires that you have passed nullptr in the constructor. 196 * 197 * @param initialBlock optional memory to use for the first block. 198 * Must be at least itemSize*itemsPerBlock sized. 199 * Caller is responsible for freeing this memory. 200 */ 201 void setInitialBlock(void* initialBlock) { 202 SkASSERT(0 == fCount); 203 SkASSERT(0 == fBlocks.count()); 204 SkASSERT(fItemsPerBlock == fInsertionIndexInBlock); 205 fOwnFirstBlock = false; 206 fBlocks.push_back() = initialBlock; 207 fInsertionIndexInBlock = 0; 208 } 209 210 // For access to above function. 211 template <typename T> friend class GrTAllocator; 212 213 private: 214 static const int NUM_INIT_BLOCK_PTRS = 8; 215 216 SkSTArray<NUM_INIT_BLOCK_PTRS, void*, true> fBlocks; 217 size_t fBlockSize; 218 size_t fItemSize; 219 int fItemsPerBlock; 220 bool fOwnFirstBlock; 221 int fCount; 222 int fInsertionIndexInBlock; 223 224 typedef SkNoncopyable INHERITED; 225 }; 226 227 template <typename T> class GrTAllocator; 228 template <typename T> void* operator new(size_t, GrTAllocator<T>*); 229 230 template <typename T> class GrTAllocator : SkNoncopyable { 231 public: 232 virtual ~GrTAllocator() { this->reset(); }; 233 234 /** 235 * Create an allocator 236 * 237 * @param itemsPerBlock the number of items to allocate at once 238 */ 239 explicit GrTAllocator(int itemsPerBlock) 240 : fAllocator(sizeof(T), itemsPerBlock, nullptr) {} 241 242 /** 243 * Adds an item and returns it. 244 * 245 * @return the added item. 246 */ 247 T& push_back() { 248 void* item = fAllocator.push_back(); 249 SkASSERT(item); 250 new (item) T; 251 return *(T*)item; 252 } 253 254 T& push_back(const T& t) { 255 void* item = fAllocator.push_back(); 256 SkASSERT(item); 257 new (item) T(t); 258 return *(T*)item; 259 } 260 261 /** 262 * Remove the last item, only call if count() != 0 263 */ 264 void pop_back() { 265 this->back().~T(); 266 fAllocator.pop_back(); 267 } 268 269 /** 270 * Removes all added items. 271 */ 272 void reset() { 273 int c = fAllocator.count(); 274 for (int i = 0; i < c; ++i) { 275 ((T*)fAllocator[i])->~T(); 276 } 277 fAllocator.reset(); 278 } 279 280 /** 281 * Returns the item count. 282 */ 283 int count() const { 284 return fAllocator.count(); 285 } 286 287 /** 288 * Is the count 0? 289 */ 290 bool empty() const { return fAllocator.empty(); } 291 292 /** 293 * Access last item, only call if count() != 0 294 */ 295 T& back() { 296 return *(T*)fAllocator.back(); 297 } 298 299 /** 300 * Access last item, only call if count() != 0 301 */ 302 const T& back() const { 303 return *(const T*)fAllocator.back(); 304 } 305 306 /** 307 * Iterates through the allocator. This is faster than using operator[] when walking linearly 308 * through the allocator. 309 */ 310 class Iter { 311 public: 312 /** 313 * Initializes the iterator. next() must be called before get() or ops * and ->. 314 */ 315 Iter(const GrTAllocator* allocator) : fImpl(&allocator->fAllocator) {} 316 317 /** 318 * Advances the iterator. Iteration is finished when next() returns false. 319 */ 320 bool next() { return fImpl.next(); } 321 322 /** 323 * Gets the current iterator value. Call next() at least once before calling. Don't call 324 * after next() returns false. 325 */ 326 T* get() const { return (T*) fImpl.get(); } 327 328 /** 329 * Convenience operators. Same rules for calling apply as get(). 330 */ 331 T& operator*() const { return *this->get(); } 332 T* operator->() const { return this->get(); } 333 334 private: 335 GrAllocator::Iter fImpl; 336 }; 337 338 /** 339 * Access item by index. 340 */ 341 T& operator[] (int i) { 342 return *(T*)(fAllocator[i]); 343 } 344 345 /** 346 * Access item by index. 347 */ 348 const T& operator[] (int i) const { 349 return *(const T*)(fAllocator[i]); 350 } 351 352 protected: 353 /* 354 * Set first block of memory to write into. Must be called before any other methods. 355 * 356 * @param initialBlock optional memory to use for the first block. 357 * Must be at least size(T)*itemsPerBlock sized. 358 * Caller is responsible for freeing this memory. 359 */ 360 void setInitialBlock(void* initialBlock) { 361 fAllocator.setInitialBlock(initialBlock); 362 } 363 364 private: 365 friend void* operator new<T>(size_t, GrTAllocator*); 366 367 GrAllocator fAllocator; 368 typedef SkNoncopyable INHERITED; 369 }; 370 371 template <int N, typename T> class GrSTAllocator : public GrTAllocator<T> { 372 private: 373 typedef GrTAllocator<T> INHERITED; 374 375 public: 376 GrSTAllocator() : INHERITED(N) { 377 this->setInitialBlock(fStorage.get()); 378 } 379 380 private: 381 SkAlignedSTStorage<N, T> fStorage; 382 }; 383 384 template <typename T> void* operator new(size_t size, GrTAllocator<T>* allocator) { 385 return allocator->fAllocator.push_back(); 386 } 387 388 // Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete 389 // to match the op new silences warnings about missing op delete when a constructor throws an 390 // exception. 391 template <typename T> void operator delete(void*, GrTAllocator<T>*) { 392 SK_ABORT("Invalid Operation"); 393 } 394 395 #define GrNEW_APPEND_TO_ALLOCATOR(allocator_ptr, type_name, args) \ 396 new (allocator_ptr) type_name args 397 398 #endif 399