Home | History | Annotate | Download | only in private
      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      */
     28     SkTArray() { this->init(); }
     29 
     30     /**
     31      * Creates an empty array that will preallocate space for reserveCount
     32      * elements.
     33      */
     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      */
     39     explicit SkTArray(const SkTArray& that) {
     40         this->init(that.fCount);
     41         this->copy(that.fItemArray);
     42     }
     43 
     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      */
     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 
     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      */
    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      */
    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      */
    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      */
    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 
    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      */
    166     int count() const { return fCount; }
    167 
    168     /**
    169      * Is the array empty.
    170      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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. */
    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 
    316     T* begin() {
    317         return fItemArray;
    318     }
    319     const T* begin() const {
    320         return fItemArray;
    321     }
    322     T* end() {
    323         return fItemArray ? fItemArray + fCount : nullptr;
    324     }
    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      */
    347     T& front() { SkASSERT(fCount > 0); return fItemArray[0];}
    348 
    349     const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];}
    350 
    351     /**
    352      * equivalent to operator[](count() - 1)
    353      */
    354     T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];}
    355 
    356     const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];}
    357 
    358     /**
    359      * equivalent to operator[](count()-1-i)
    360      */
    361     T& fromBack(int i) {
    362         SkASSERT(i >= 0);
    363         SkASSERT(i < fCount);
    364         return fItemArray[fCount - i - 1];
    365     }
    366 
    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>
    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>
    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>
    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>
    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 
    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      */
    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 
    485     template <bool E = MEM_MOVE> SK_WHEN(E, void) move(int dst, int src) {
    486         memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T));
    487     }
    488     template <bool E = MEM_MOVE> SK_WHEN(E, void) move(void* dst) {
    489         sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T));
    490     }
    491 
    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     }
    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.
    507     void* push_back_raw(int n) {
    508         this->checkRealloc(n);
    509         void* ptr = fItemArray + fCount;
    510         fCount += n;
    511         return ptr;
    512     }
    513 
    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:
    572     SkSTArray() : INHERITED(&fStorage) {
    573     }
    574 
    575     SkSTArray(const SkSTArray& array)
    576         : INHERITED(array, &fStorage) {
    577     }
    578 
    579     SkSTArray(SkSTArray&& array)
    580         : INHERITED(std::move(array), &fStorage) {
    581     }
    582 
    583     explicit SkSTArray(const INHERITED& array)
    584         : INHERITED(array, &fStorage) {
    585     }
    586 
    587     explicit SkSTArray(INHERITED&& array)
    588         : INHERITED(std::move(array), &fStorage) {
    589     }
    590 
    591     explicit SkSTArray(int reserveCount)
    592         : INHERITED(reserveCount) {
    593     }
    594 
    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