1 /* 2 * Copyright 2011 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 SkTArray_DEFINED 9 #define SkTArray_DEFINED 10 11 #include "../private/SkTLogic.h" 12 #include "../private/SkTemplates.h" 13 #include "SkTypes.h" 14 15 #include <new> 16 #include <utility> 17 18 /** When MEM_MOVE is true T will be bit copied when moved. 19 When MEM_MOVE is false, T will be copy constructed / destructed. 20 In all cases T will be default-initialized on allocation, 21 and its destructor will be called from this object's destructor. 22 */ 23 template <typename T, bool MEM_MOVE = false> class SkTArray { 24 public: 25 /** 26 * Creates an empty array with no initial storage 27 */ SkTArray()28 SkTArray() { this->init(); } 29 30 /** 31 * Creates an empty array that will preallocate space for reserveCount 32 * elements. 33 */ SkTArray(int reserveCount)34 explicit SkTArray(int reserveCount) { this->init(0, reserveCount); } 35 36 /** 37 * Copies one array to another. The new array will be heap allocated. 38 */ SkTArray(const SkTArray & that)39 explicit SkTArray(const SkTArray& that) { 40 this->init(that.fCount); 41 this->copy(that.fItemArray); 42 } 43 SkTArray(SkTArray && that)44 explicit SkTArray(SkTArray&& that) { 45 // TODO: If 'that' owns its memory why don't we just steal the pointer? 46 this->init(that.fCount); 47 that.move(fMemArray); 48 that.fCount = 0; 49 } 50 51 /** 52 * Creates a SkTArray by copying contents of a standard C array. The new 53 * array will be heap allocated. Be careful not to use this constructor 54 * when you really want the (void*, int) version. 55 */ SkTArray(const T * array,int count)56 SkTArray(const T* array, int count) { 57 this->init(count); 58 this->copy(array); 59 } 60 61 SkTArray& operator=(const SkTArray& that) { 62 if (this == &that) { 63 return *this; 64 } 65 for (int i = 0; i < fCount; ++i) { 66 fItemArray[i].~T(); 67 } 68 fCount = 0; 69 this->checkRealloc(that.count()); 70 fCount = that.count(); 71 this->copy(that.fItemArray); 72 return *this; 73 } 74 SkTArray& operator=(SkTArray&& that) { 75 if (this == &that) { 76 return *this; 77 } 78 for (int i = 0; i < fCount; ++i) { 79 fItemArray[i].~T(); 80 } 81 fCount = 0; 82 this->checkRealloc(that.count()); 83 fCount = that.count(); 84 that.move(fMemArray); 85 that.fCount = 0; 86 return *this; 87 } 88 ~SkTArray()89 ~SkTArray() { 90 for (int i = 0; i < fCount; ++i) { 91 fItemArray[i].~T(); 92 } 93 if (fOwnMemory) { 94 sk_free(fMemArray); 95 } 96 } 97 98 /** 99 * Resets to count() == 0 and resets any reserve count. 100 */ reset()101 void reset() { 102 this->pop_back_n(fCount); 103 fReserved = false; 104 } 105 106 /** 107 * Resets to count() = n newly constructed T objects and resets any reserve count. 108 */ reset(int n)109 void reset(int n) { 110 SkASSERT(n >= 0); 111 for (int i = 0; i < fCount; ++i) { 112 fItemArray[i].~T(); 113 } 114 // Set fCount to 0 before calling checkRealloc so that no elements are moved. 115 fCount = 0; 116 this->checkRealloc(n); 117 fCount = n; 118 for (int i = 0; i < fCount; ++i) { 119 new (fItemArray + i) T; 120 } 121 fReserved = false; 122 } 123 124 /** 125 * Resets to a copy of a C array and resets any reserve count. 126 */ reset(const T * array,int count)127 void reset(const T* array, int count) { 128 for (int i = 0; i < fCount; ++i) { 129 fItemArray[i].~T(); 130 } 131 fCount = 0; 132 this->checkRealloc(count); 133 fCount = count; 134 this->copy(array); 135 fReserved = false; 136 } 137 138 /** 139 * Ensures there is enough reserved space for n additional elements. The is guaranteed at least 140 * until the array size grows above n and subsequently shrinks below n, any version of reset() 141 * is called, or reserve() is called again. 142 */ reserve(int n)143 void reserve(int n) { 144 SkASSERT(n >= 0); 145 if (n > 0) { 146 this->checkRealloc(n); 147 fReserved = fOwnMemory; 148 } else { 149 fReserved = false; 150 } 151 } 152 removeShuffle(int n)153 void removeShuffle(int n) { 154 SkASSERT(n < fCount); 155 int newCount = fCount - 1; 156 fCount = newCount; 157 fItemArray[n].~T(); 158 if (n != newCount) { 159 this->move(n, newCount); 160 } 161 } 162 163 /** 164 * Number of elements in the array. 165 */ count()166 int count() const { return fCount; } 167 168 /** 169 * Is the array empty. 170 */ empty()171 bool empty() const { return !fCount; } 172 173 /** 174 * Adds 1 new default-initialized T value and returns it by reference. Note 175 * the reference only remains valid until the next call that adds or removes 176 * elements. 177 */ push_back()178 T& push_back() { 179 void* newT = this->push_back_raw(1); 180 return *new (newT) T; 181 } 182 183 /** 184 * Version of above that uses a copy constructor to initialize the new item 185 */ push_back(const T & t)186 T& push_back(const T& t) { 187 void* newT = this->push_back_raw(1); 188 return *new (newT) T(t); 189 } 190 191 /** 192 * Version of above that uses a move constructor to initialize the new item 193 */ push_back(T && t)194 T& push_back(T&& t) { 195 void* newT = this->push_back_raw(1); 196 return *new (newT) T(std::move(t)); 197 } 198 199 /** 200 * Construct a new T at the back of this array. 201 */ emplace_back(Args &&...args)202 template<class... Args> T& emplace_back(Args&&... args) { 203 void* newT = this->push_back_raw(1); 204 return *new (newT) T(std::forward<Args>(args)...); 205 } 206 207 /** 208 * Allocates n more default-initialized T values, and returns the address of 209 * the start of that new range. Note: this address is only valid until the 210 * next API call made on the array that might add or remove elements. 211 */ push_back_n(int n)212 T* push_back_n(int n) { 213 SkASSERT(n >= 0); 214 void* newTs = this->push_back_raw(n); 215 for (int i = 0; i < n; ++i) { 216 new (static_cast<char*>(newTs) + i * sizeof(T)) T; 217 } 218 return static_cast<T*>(newTs); 219 } 220 221 /** 222 * Version of above that uses a copy constructor to initialize all n items 223 * to the same T. 224 */ push_back_n(int n,const T & t)225 T* push_back_n(int n, const T& t) { 226 SkASSERT(n >= 0); 227 void* newTs = this->push_back_raw(n); 228 for (int i = 0; i < n; ++i) { 229 new (static_cast<char*>(newTs) + i * sizeof(T)) T(t); 230 } 231 return static_cast<T*>(newTs); 232 } 233 234 /** 235 * Version of above that uses a copy constructor to initialize the n items 236 * to separate T values. 237 */ push_back_n(int n,const T t[])238 T* push_back_n(int n, const T t[]) { 239 SkASSERT(n >= 0); 240 this->checkRealloc(n); 241 for (int i = 0; i < n; ++i) { 242 new (fItemArray + fCount + i) T(t[i]); 243 } 244 fCount += n; 245 return fItemArray + fCount - n; 246 } 247 248 /** 249 * Version of above that uses the move constructor to set n items. 250 */ move_back_n(int n,T * t)251 T* move_back_n(int n, T* t) { 252 SkASSERT(n >= 0); 253 this->checkRealloc(n); 254 for (int i = 0; i < n; ++i) { 255 new (fItemArray + fCount + i) T(std::move(t[i])); 256 } 257 fCount += n; 258 return fItemArray + fCount - n; 259 } 260 261 /** 262 * Removes the last element. Not safe to call when count() == 0. 263 */ pop_back()264 void pop_back() { 265 SkASSERT(fCount > 0); 266 --fCount; 267 fItemArray[fCount].~T(); 268 this->checkRealloc(0); 269 } 270 271 /** 272 * Removes the last n elements. Not safe to call when count() < n. 273 */ pop_back_n(int n)274 void pop_back_n(int n) { 275 SkASSERT(n >= 0); 276 SkASSERT(fCount >= n); 277 fCount -= n; 278 for (int i = 0; i < n; ++i) { 279 fItemArray[fCount + i].~T(); 280 } 281 this->checkRealloc(0); 282 } 283 284 /** 285 * Pushes or pops from the back to resize. Pushes will be default 286 * initialized. 287 */ resize_back(int newCount)288 void resize_back(int newCount) { 289 SkASSERT(newCount >= 0); 290 291 if (newCount > fCount) { 292 this->push_back_n(newCount - fCount); 293 } else if (newCount < fCount) { 294 this->pop_back_n(fCount - newCount); 295 } 296 } 297 298 /** Swaps the contents of this array with that array. Does a pointer swap if possible, 299 otherwise copies the T values. */ swap(SkTArray * that)300 void swap(SkTArray* that) { 301 if (this == that) { 302 return; 303 } 304 if (fOwnMemory && that->fOwnMemory) { 305 SkTSwap(fItemArray, that->fItemArray); 306 SkTSwap(fCount, that->fCount); 307 SkTSwap(fAllocCount, that->fAllocCount); 308 } else { 309 // This could be more optimal... 310 SkTArray copy(std::move(*that)); 311 *that = std::move(*this); 312 *this = std::move(copy); 313 } 314 } 315 begin()316 T* begin() { 317 return fItemArray; 318 } begin()319 const T* begin() const { 320 return fItemArray; 321 } end()322 T* end() { 323 return fItemArray ? fItemArray + fCount : nullptr; 324 } end()325 const T* end() const { 326 return fItemArray ? fItemArray + fCount : nullptr; 327 } 328 329 /** 330 * Get the i^th element. 331 */ 332 T& operator[] (int i) { 333 SkASSERT(i < fCount); 334 SkASSERT(i >= 0); 335 return fItemArray[i]; 336 } 337 338 const T& operator[] (int i) const { 339 SkASSERT(i < fCount); 340 SkASSERT(i >= 0); 341 return fItemArray[i]; 342 } 343 344 /** 345 * equivalent to operator[](0) 346 */ front()347 T& front() { SkASSERT(fCount > 0); return fItemArray[0];} 348 front()349 const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];} 350 351 /** 352 * equivalent to operator[](count() - 1) 353 */ back()354 T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];} 355 back()356 const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];} 357 358 /** 359 * equivalent to operator[](count()-1-i) 360 */ fromBack(int i)361 T& fromBack(int i) { 362 SkASSERT(i >= 0); 363 SkASSERT(i < fCount); 364 return fItemArray[fCount - i - 1]; 365 } 366 fromBack(int i)367 const T& fromBack(int i) const { 368 SkASSERT(i >= 0); 369 SkASSERT(i < fCount); 370 return fItemArray[fCount - i - 1]; 371 } 372 373 bool operator==(const SkTArray<T, MEM_MOVE>& right) const { 374 int leftCount = this->count(); 375 if (leftCount != right.count()) { 376 return false; 377 } 378 for (int index = 0; index < leftCount; ++index) { 379 if (fItemArray[index] != right.fItemArray[index]) { 380 return false; 381 } 382 } 383 return true; 384 } 385 386 bool operator!=(const SkTArray<T, MEM_MOVE>& right) const { 387 return !(*this == right); 388 } 389 390 inline int allocCntForTest() const; 391 392 protected: 393 /** 394 * Creates an empty array that will use the passed storage block until it 395 * is insufficiently large to hold the entire array. 396 */ 397 template <int N> SkTArray(SkAlignedSTStorage<N,T> * storage)398 SkTArray(SkAlignedSTStorage<N,T>* storage) { 399 this->initWithPreallocatedStorage(0, storage->get(), N); 400 } 401 402 /** 403 * Copy another array, using preallocated storage if preAllocCount >= 404 * array.count(). Otherwise storage will only be used when array shrinks 405 * to fit. 406 */ 407 template <int N> SkTArray(const SkTArray & array,SkAlignedSTStorage<N,T> * storage)408 SkTArray(const SkTArray& array, SkAlignedSTStorage<N,T>* storage) { 409 this->initWithPreallocatedStorage(array.fCount, storage->get(), N); 410 this->copy(array.fItemArray); 411 } 412 413 /** 414 * Move another array, using preallocated storage if preAllocCount >= 415 * array.count(). Otherwise storage will only be used when array shrinks 416 * to fit. 417 */ 418 template <int N> SkTArray(SkTArray && array,SkAlignedSTStorage<N,T> * storage)419 SkTArray(SkTArray&& array, SkAlignedSTStorage<N,T>* storage) { 420 this->initWithPreallocatedStorage(array.fCount, storage->get(), N); 421 array.move(fMemArray); 422 array.fCount = 0; 423 } 424 425 /** 426 * Copy a C array, using preallocated storage if preAllocCount >= 427 * count. Otherwise storage will only be used when array shrinks 428 * to fit. 429 */ 430 template <int N> SkTArray(const T * array,int count,SkAlignedSTStorage<N,T> * storage)431 SkTArray(const T* array, int count, SkAlignedSTStorage<N,T>* storage) { 432 this->initWithPreallocatedStorage(count, storage->get(), N); 433 this->copy(array); 434 } 435 436 private: 437 void init(int count = 0, int reserveCount = 0) { 438 SkASSERT(count >= 0); 439 SkASSERT(reserveCount >= 0); 440 fCount = count; 441 if (!count && !reserveCount) { 442 fAllocCount = 0; 443 fMemArray = nullptr; 444 fOwnMemory = true; 445 fReserved = false; 446 } else { 447 fAllocCount = SkTMax(count, SkTMax(kMinHeapAllocCount, reserveCount)); 448 fMemArray = sk_malloc_throw(fAllocCount, sizeof(T)); 449 fOwnMemory = true; 450 fReserved = reserveCount > 0; 451 } 452 } 453 initWithPreallocatedStorage(int count,void * preallocStorage,int preallocCount)454 void initWithPreallocatedStorage(int count, void* preallocStorage, int preallocCount) { 455 SkASSERT(count >= 0); 456 SkASSERT(preallocCount > 0); 457 SkASSERT(preallocStorage); 458 fCount = count; 459 fMemArray = nullptr; 460 fReserved = false; 461 if (count > preallocCount) { 462 fAllocCount = SkTMax(count, kMinHeapAllocCount); 463 fMemArray = sk_malloc_throw(fAllocCount, sizeof(T)); 464 fOwnMemory = true; 465 } else { 466 fAllocCount = preallocCount; 467 fMemArray = preallocStorage; 468 fOwnMemory = false; 469 } 470 } 471 472 /** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage. 473 * In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage. 474 */ copy(const T * src)475 void copy(const T* src) { 476 // Some types may be trivially copyable, in which case we *could* use memcopy; but 477 // MEM_MOVE == true implies that the type is trivially movable, and not necessarily 478 // trivially copyable (think sk_sp<>). So short of adding another template arg, we 479 // must be conservative and use copy construction. 480 for (int i = 0; i < fCount; ++i) { 481 new (fItemArray + i) T(src[i]); 482 } 483 } 484 SK_WHEN(E,void)485 template <bool E = MEM_MOVE> SK_WHEN(E, void) move(int dst, int src) { 486 memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T)); 487 } SK_WHEN(E,void)488 template <bool E = MEM_MOVE> SK_WHEN(E, void) move(void* dst) { 489 sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T)); 490 } 491 move(int dst,int src)492 template <bool E = MEM_MOVE> SK_WHEN(!E, void) move(int dst, int src) { 493 new (&fItemArray[dst]) T(std::move(fItemArray[src])); 494 fItemArray[src].~T(); 495 } move(void * dst)496 template <bool E = MEM_MOVE> SK_WHEN(!E, void) move(void* dst) { 497 for (int i = 0; i < fCount; ++i) { 498 new (static_cast<char*>(dst) + sizeof(T) * i) T(std::move(fItemArray[i])); 499 fItemArray[i].~T(); 500 } 501 } 502 503 static constexpr int kMinHeapAllocCount = 8; 504 505 // Helper function that makes space for n objects, adjusts the count, but does not initialize 506 // the new objects. push_back_raw(int n)507 void* push_back_raw(int n) { 508 this->checkRealloc(n); 509 void* ptr = fItemArray + fCount; 510 fCount += n; 511 return ptr; 512 } 513 checkRealloc(int delta)514 void checkRealloc(int delta) { 515 SkASSERT(fCount >= 0); 516 SkASSERT(fAllocCount >= 0); 517 SkASSERT(-delta <= fCount); 518 519 int newCount = fCount + delta; 520 521 // We allow fAllocCount to be in the range [newCount, 3*newCount]. We also never shrink 522 // when we're currently using preallocated memory, would allocate less than 523 // kMinHeapAllocCount, or a reserve count was specified that has yet to be exceeded. 524 bool mustGrow = newCount > fAllocCount; 525 bool shouldShrink = fAllocCount > 3 * newCount && fOwnMemory && !fReserved; 526 if (!mustGrow && !shouldShrink) { 527 return; 528 } 529 530 // Whether we're growing or shrinking, we leave at least 50% extra space for future growth. 531 int newAllocCount = newCount + ((newCount + 1) >> 1); 532 // Align the new allocation count to kMinHeapAllocCount. 533 static_assert(SkIsPow2(kMinHeapAllocCount), "min alloc count not power of two."); 534 newAllocCount = (newAllocCount + (kMinHeapAllocCount - 1)) & ~(kMinHeapAllocCount - 1); 535 // At small sizes the old and new alloc count can both be kMinHeapAllocCount. 536 if (newAllocCount == fAllocCount) { 537 return; 538 } 539 fAllocCount = newAllocCount; 540 void* newMemArray = sk_malloc_throw(fAllocCount, sizeof(T)); 541 this->move(newMemArray); 542 if (fOwnMemory) { 543 sk_free(fMemArray); 544 545 } 546 fMemArray = newMemArray; 547 fOwnMemory = true; 548 fReserved = false; 549 } 550 551 union { 552 T* fItemArray; 553 void* fMemArray; 554 }; 555 int fCount; 556 int fAllocCount; 557 bool fOwnMemory : 1; 558 bool fReserved : 1; 559 }; 560 561 template<typename T, bool MEM_MOVE> constexpr int SkTArray<T, MEM_MOVE>::kMinHeapAllocCount; 562 563 /** 564 * Subclass of SkTArray that contains a preallocated memory block for the array. 565 */ 566 template <int N, typename T, bool MEM_MOVE= false> 567 class SkSTArray : public SkTArray<T, MEM_MOVE> { 568 private: 569 typedef SkTArray<T, MEM_MOVE> INHERITED; 570 571 public: SkSTArray()572 SkSTArray() : INHERITED(&fStorage) { 573 } 574 SkSTArray(const SkSTArray & array)575 SkSTArray(const SkSTArray& array) 576 : INHERITED(array, &fStorage) { 577 } 578 SkSTArray(SkSTArray && array)579 SkSTArray(SkSTArray&& array) 580 : INHERITED(std::move(array), &fStorage) { 581 } 582 SkSTArray(const INHERITED & array)583 explicit SkSTArray(const INHERITED& array) 584 : INHERITED(array, &fStorage) { 585 } 586 SkSTArray(INHERITED && array)587 explicit SkSTArray(INHERITED&& array) 588 : INHERITED(std::move(array), &fStorage) { 589 } 590 SkSTArray(int reserveCount)591 explicit SkSTArray(int reserveCount) 592 : INHERITED(reserveCount) { 593 } 594 SkSTArray(const T * array,int count)595 SkSTArray(const T* array, int count) 596 : INHERITED(array, count, &fStorage) { 597 } 598 599 SkSTArray& operator=(const SkSTArray& array) { 600 INHERITED::operator=(array); 601 return *this; 602 } 603 604 SkSTArray& operator=(SkSTArray&& array) { 605 INHERITED::operator=(std::move(array)); 606 return *this; 607 } 608 609 SkSTArray& operator=(const INHERITED& array) { 610 INHERITED::operator=(array); 611 return *this; 612 } 613 614 SkSTArray& operator=(INHERITED&& array) { 615 INHERITED::operator=(std::move(array)); 616 return *this; 617 } 618 619 private: 620 SkAlignedSTStorage<N,T> fStorage; 621 }; 622 623 #endif 624