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_COPY is true T will be bit copied when moved.
     19     When MEM_COPY 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_COPY = false> class SkTArray {
     24 public:
     25     /**
     26      * Creates an empty array with no initial storage
     27      */
     28     SkTArray() {
     29         fCount = 0;
     30         fReserveCount = gMIN_ALLOC_COUNT;
     31         fAllocCount = 0;
     32         fMemArray = NULL;
     33         fPreAllocMemArray = NULL;
     34     }
     35 
     36     /**
     37      * Creates an empty array that will preallocate space for reserveCount
     38      * elements.
     39      */
     40     explicit SkTArray(int reserveCount) {
     41         this->init(NULL, 0, NULL, reserveCount);
     42     }
     43 
     44     /**
     45      * Copies one array to another. The new array will be heap allocated.
     46      */
     47     explicit SkTArray(const SkTArray& array) {
     48         this->init(array.fItemArray, array.fCount, NULL, 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(array, count, NULL, 0);
     58     }
     59 
     60     /**
     61      * assign copy of array to this
     62      */
     63     SkTArray& operator =(const SkTArray& array) {
     64         for (int i = 0; i < fCount; ++i) {
     65             fItemArray[i].~T();
     66         }
     67         fCount = 0;
     68         this->checkRealloc((int)array.count());
     69         fCount = array.count();
     70         this->copy(static_cast<const T*>(array.fMemArray));
     71         return *this;
     72     }
     73 
     74     ~SkTArray() {
     75         for (int i = 0; i < fCount; ++i) {
     76             fItemArray[i].~T();
     77         }
     78         if (fMemArray != fPreAllocMemArray) {
     79             sk_free(fMemArray);
     80         }
     81     }
     82 
     83     /**
     84      * Resets to count() == 0
     85      */
     86     void reset() { this->pop_back_n(fCount); }
     87 
     88     /**
     89      * Resets to count() = n newly constructed T objects.
     90      */
     91     void reset(int n) {
     92         SkASSERT(n >= 0);
     93         for (int i = 0; i < fCount; ++i) {
     94             fItemArray[i].~T();
     95         }
     96         // set fCount to 0 before calling checkRealloc so that no copy cons. are called.
     97         fCount = 0;
     98         this->checkRealloc(n);
     99         fCount = n;
    100         for (int i = 0; i < fCount; ++i) {
    101             new (fItemArray + i) T;
    102         }
    103     }
    104 
    105     /**
    106      * Resets to a copy of a C array.
    107      */
    108     void reset(const T* array, int count) {
    109         for (int i = 0; i < fCount; ++i) {
    110             fItemArray[i].~T();
    111         }
    112         int delta = count - fCount;
    113         this->checkRealloc(delta);
    114         fCount = count;
    115         this->copy(array);
    116     }
    117 
    118     void removeShuffle(int n) {
    119         SkASSERT(n < fCount);
    120         int newCount = fCount - 1;
    121         fCount = newCount;
    122         fItemArray[n].~T();
    123         if (n != newCount) {
    124             this->move(n, newCount);
    125         }
    126     }
    127 
    128     /**
    129      * Number of elements in the array.
    130      */
    131     int count() const { return fCount; }
    132 
    133     /**
    134      * Is the array empty.
    135      */
    136     bool empty() const { return !fCount; }
    137 
    138     /**
    139      * Adds 1 new default-initialized T value and returns it by reference. Note
    140      * the reference only remains valid until the next call that adds or removes
    141      * elements.
    142      */
    143     T& push_back() {
    144         T* newT = reinterpret_cast<T*>(this->push_back_raw(1));
    145         new (newT) T;
    146         return *newT;
    147     }
    148 
    149     /**
    150      * Version of above that uses a copy constructor to initialize the new item
    151      */
    152     T& push_back(const T& t) {
    153         T* newT = reinterpret_cast<T*>(this->push_back_raw(1));
    154         new (newT) T(t);
    155         return *newT;
    156     }
    157 
    158     /**
    159      *  Construct a new T at the back of this array.
    160      */
    161     template<class... Args> T& emplace_back(Args&&... args) {
    162         T* newT = reinterpret_cast<T*>(this->push_back_raw(1));
    163         return *new (newT) T(std::forward<Args>(args)...);
    164     }
    165 
    166     /**
    167      * Allocates n more default-initialized T values, and returns the address of
    168      * the start of that new range. Note: this address is only valid until the
    169      * next API call made on the array that might add or remove elements.
    170      */
    171     T* push_back_n(int n) {
    172         SkASSERT(n >= 0);
    173         T* newTs = reinterpret_cast<T*>(this->push_back_raw(n));
    174         for (int i = 0; i < n; ++i) {
    175             new (newTs + i) T;
    176         }
    177         return newTs;
    178     }
    179 
    180     /**
    181      * Version of above that uses a copy constructor to initialize all n items
    182      * to the same T.
    183      */
    184     T* push_back_n(int n, const T& t) {
    185         SkASSERT(n >= 0);
    186         T* newTs = reinterpret_cast<T*>(this->push_back_raw(n));
    187         for (int i = 0; i < n; ++i) {
    188             new (newTs[i]) T(t);
    189         }
    190         return newTs;
    191     }
    192 
    193     /**
    194      * Version of above that uses a copy constructor to initialize the n items
    195      * to separate T values.
    196      */
    197     T* push_back_n(int n, const T t[]) {
    198         SkASSERT(n >= 0);
    199         this->checkRealloc(n);
    200         for (int i = 0; i < n; ++i) {
    201             new (fItemArray + fCount + i) T(t[i]);
    202         }
    203         fCount += n;
    204         return fItemArray + fCount - n;
    205     }
    206 
    207     /**
    208      * Removes the last element. Not safe to call when count() == 0.
    209      */
    210     void pop_back() {
    211         SkASSERT(fCount > 0);
    212         --fCount;
    213         fItemArray[fCount].~T();
    214         this->checkRealloc(0);
    215     }
    216 
    217     /**
    218      * Removes the last n elements. Not safe to call when count() < n.
    219      */
    220     void pop_back_n(int n) {
    221         SkASSERT(n >= 0);
    222         SkASSERT(fCount >= n);
    223         fCount -= n;
    224         for (int i = 0; i < n; ++i) {
    225             fItemArray[fCount + i].~T();
    226         }
    227         this->checkRealloc(0);
    228     }
    229 
    230     /**
    231      * Pushes or pops from the back to resize. Pushes will be default
    232      * initialized.
    233      */
    234     void resize_back(int newCount) {
    235         SkASSERT(newCount >= 0);
    236 
    237         if (newCount > fCount) {
    238             this->push_back_n(newCount - fCount);
    239         } else if (newCount < fCount) {
    240             this->pop_back_n(fCount - newCount);
    241         }
    242     }
    243 
    244     /** Swaps the contents of this array with that array. Does a pointer swap if possible,
    245         otherwise copies the T values. */
    246     void swap(SkTArray* that) {
    247         if (this == that) {
    248             return;
    249         }
    250         if (this->fPreAllocMemArray != this->fItemArray &&
    251             that->fPreAllocMemArray != that->fItemArray) {
    252             // If neither is using a preallocated array then just swap.
    253             SkTSwap(fItemArray, that->fItemArray);
    254             SkTSwap(fCount, that->fCount);
    255             SkTSwap(fAllocCount, that->fAllocCount);
    256         } else {
    257             // This could be more optimal...
    258             SkTArray copy(*that);
    259             *that = *this;
    260             *this = copy;
    261         }
    262     }
    263 
    264     T* begin() {
    265         return fItemArray;
    266     }
    267     const T* begin() const {
    268         return fItemArray;
    269     }
    270     T* end() {
    271         return fItemArray ? fItemArray + fCount : NULL;
    272     }
    273     const T* end() const {
    274         return fItemArray ? fItemArray + fCount : NULL;
    275     }
    276 
    277    /**
    278      * Get the i^th element.
    279      */
    280     T& operator[] (int i) {
    281         SkASSERT(i < fCount);
    282         SkASSERT(i >= 0);
    283         return fItemArray[i];
    284     }
    285 
    286     const T& operator[] (int i) const {
    287         SkASSERT(i < fCount);
    288         SkASSERT(i >= 0);
    289         return fItemArray[i];
    290     }
    291 
    292     /**
    293      * equivalent to operator[](0)
    294      */
    295     T& front() { SkASSERT(fCount > 0); return fItemArray[0];}
    296 
    297     const T& front() const { SkASSERT(fCount > 0); return fItemArray[0];}
    298 
    299     /**
    300      * equivalent to operator[](count() - 1)
    301      */
    302     T& back() { SkASSERT(fCount); return fItemArray[fCount - 1];}
    303 
    304     const T& back() const { SkASSERT(fCount > 0); return fItemArray[fCount - 1];}
    305 
    306     /**
    307      * equivalent to operator[](count()-1-i)
    308      */
    309     T& fromBack(int i) {
    310         SkASSERT(i >= 0);
    311         SkASSERT(i < fCount);
    312         return fItemArray[fCount - i - 1];
    313     }
    314 
    315     const T& fromBack(int i) const {
    316         SkASSERT(i >= 0);
    317         SkASSERT(i < fCount);
    318         return fItemArray[fCount - i - 1];
    319     }
    320 
    321     bool operator==(const SkTArray<T, MEM_COPY>& right) const {
    322         int leftCount = this->count();
    323         if (leftCount != right.count()) {
    324             return false;
    325         }
    326         for (int index = 0; index < leftCount; ++index) {
    327             if (fItemArray[index] != right.fItemArray[index]) {
    328                 return false;
    329             }
    330         }
    331         return true;
    332     }
    333 
    334     bool operator!=(const SkTArray<T, MEM_COPY>& right) const {
    335         return !(*this == right);
    336     }
    337 
    338 protected:
    339     /**
    340      * Creates an empty array that will use the passed storage block until it
    341      * is insufficiently large to hold the entire array.
    342      */
    343     template <int N>
    344     SkTArray(SkAlignedSTStorage<N,T>* storage) {
    345         this->init(NULL, 0, storage->get(), N);
    346     }
    347 
    348     /**
    349      * Copy another array, using preallocated storage if preAllocCount >=
    350      * array.count(). Otherwise storage will only be used when array shrinks
    351      * to fit.
    352      */
    353     template <int N>
    354     SkTArray(const SkTArray& array, SkAlignedSTStorage<N,T>* storage) {
    355         this->init(array.fItemArray, array.fCount, storage->get(), N);
    356     }
    357 
    358     /**
    359      * Copy a C array, using preallocated storage if preAllocCount >=
    360      * count. Otherwise storage will only be used when array shrinks
    361      * to fit.
    362      */
    363     template <int N>
    364     SkTArray(const T* array, int count, SkAlignedSTStorage<N,T>* storage) {
    365         this->init(array, count, storage->get(), N);
    366     }
    367 
    368     void init(const T* array, int count,
    369               void* preAllocStorage, int preAllocOrReserveCount) {
    370         SkASSERT(count >= 0);
    371         SkASSERT(preAllocOrReserveCount >= 0);
    372         fCount              = count;
    373         fReserveCount       = (preAllocOrReserveCount > 0) ?
    374                                     preAllocOrReserveCount :
    375                                     gMIN_ALLOC_COUNT;
    376         fPreAllocMemArray   = preAllocStorage;
    377         if (fReserveCount >= fCount &&
    378             preAllocStorage) {
    379             fAllocCount = fReserveCount;
    380             fMemArray = preAllocStorage;
    381         } else {
    382             fAllocCount = SkMax32(fCount, fReserveCount);
    383             fMemArray = sk_malloc_throw(fAllocCount * sizeof(T));
    384         }
    385 
    386         this->copy(array);
    387     }
    388 
    389 private:
    390     /** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage.
    391      *  In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage.
    392      */
    393     template <bool E = MEM_COPY> SK_WHEN(E, void) copy(const T* src) {
    394         sk_careful_memcpy(fMemArray, src, fCount * sizeof(T));
    395     }
    396     template <bool E = MEM_COPY> SK_WHEN(E, void) move(int dst, int src) {
    397         memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T));
    398     }
    399     template <bool E = MEM_COPY> SK_WHEN(E, void) move(char* dst) {
    400         sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T));
    401     }
    402 
    403     template <bool E = MEM_COPY> SK_WHEN(!E, void) copy(const T* src) {
    404         for (int i = 0; i < fCount; ++i) {
    405             new (fItemArray + i) T(src[i]);
    406         }
    407     }
    408     template <bool E = MEM_COPY> SK_WHEN(!E, void) move(int dst, int src) {
    409         new (&fItemArray[dst]) T(std::move(fItemArray[src]));
    410         fItemArray[src].~T();
    411     }
    412     template <bool E = MEM_COPY> SK_WHEN(!E, void) move(char* dst) {
    413         for (int i = 0; i < fCount; ++i) {
    414             new (dst + sizeof(T) * i) T(std::move(fItemArray[i]));
    415             fItemArray[i].~T();
    416         }
    417     }
    418 
    419     static const int gMIN_ALLOC_COUNT = 8;
    420 
    421     // Helper function that makes space for n objects, adjusts the count, but does not initialize
    422     // the new objects.
    423     void* push_back_raw(int n) {
    424         this->checkRealloc(n);
    425         void* ptr = fItemArray + fCount;
    426         fCount += n;
    427         return ptr;
    428     }
    429 
    430     inline void checkRealloc(int delta) {
    431         SkASSERT(fCount >= 0);
    432         SkASSERT(fAllocCount >= 0);
    433 
    434         SkASSERT(-delta <= fCount);
    435 
    436         int newCount = fCount + delta;
    437         int newAllocCount = fAllocCount;
    438 
    439         if (newCount > fAllocCount || newCount < (fAllocCount / 3)) {
    440             // whether we're growing or shrinking, we leave at least 50% extra space for future
    441             // growth (clamped to the reserve count).
    442             newAllocCount = SkMax32(newCount + ((newCount + 1) >> 1), fReserveCount);
    443         }
    444         if (newAllocCount != fAllocCount) {
    445 
    446             fAllocCount = newAllocCount;
    447             char* newMemArray;
    448 
    449             if (fAllocCount == fReserveCount && fPreAllocMemArray) {
    450                 newMemArray = (char*) fPreAllocMemArray;
    451             } else {
    452                 newMemArray = (char*) sk_malloc_throw(fAllocCount*sizeof(T));
    453             }
    454 
    455             this->move(newMemArray);
    456 
    457             if (fMemArray != fPreAllocMemArray) {
    458                 sk_free(fMemArray);
    459             }
    460             fMemArray = newMemArray;
    461         }
    462     }
    463 
    464     int     fReserveCount;
    465     int     fCount;
    466     int     fAllocCount;
    467     void*   fPreAllocMemArray;
    468     union {
    469         T*       fItemArray;
    470         void*    fMemArray;
    471     };
    472 };
    473 
    474 /**
    475  * Subclass of SkTArray that contains a preallocated memory block for the array.
    476  */
    477 template <int N, typename T, bool MEM_COPY = false>
    478 class SkSTArray : public SkTArray<T, MEM_COPY> {
    479 private:
    480     typedef SkTArray<T, MEM_COPY> INHERITED;
    481 
    482 public:
    483     SkSTArray() : INHERITED(&fStorage) {
    484     }
    485 
    486     SkSTArray(const SkSTArray& array)
    487         : INHERITED(array, &fStorage) {
    488     }
    489 
    490     explicit SkSTArray(const INHERITED& array)
    491         : INHERITED(array, &fStorage) {
    492     }
    493 
    494     explicit SkSTArray(int reserveCount)
    495         : INHERITED(reserveCount) {
    496     }
    497 
    498     SkSTArray(const T* array, int count)
    499         : INHERITED(array, count, &fStorage) {
    500     }
    501 
    502     SkSTArray& operator= (const SkSTArray& array) {
    503         return *this = *(const INHERITED*)&array;
    504     }
    505 
    506     SkSTArray& operator= (const INHERITED& array) {
    507         INHERITED::operator=(array);
    508         return *this;
    509     }
    510 
    511 private:
    512     SkAlignedSTStorage<N,T> fStorage;
    513 };
    514 
    515 #endif
    516