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