Home | History | Annotate | Download | only in private
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #ifndef SkTemplates_DEFINED
     11 #define SkTemplates_DEFINED
     12 
     13 #include "SkMath.h"
     14 #include "SkTLogic.h"
     15 #include "SkTypes.h"
     16 #include "SkUniquePtr.h"
     17 #include <limits.h>
     18 #include <new>
     19 
     20 /** \file SkTemplates.h
     21 
     22     This file contains light-weight template classes for type-safe and exception-safe
     23     resource management.
     24 */
     25 
     26 /**
     27  *  Marks a local variable as known to be unused (to avoid warnings).
     28  *  Note that this does *not* prevent the local variable from being optimized away.
     29  */
     30 template<typename T> inline void sk_ignore_unused_variable(const T&) { }
     31 
     32 /**
     33  *  Returns a pointer to a D which comes immediately after S[count].
     34  */
     35 template <typename D, typename S> static D* SkTAfter(S* ptr, size_t count = 1) {
     36     return reinterpret_cast<D*>(ptr + count);
     37 }
     38 
     39 /**
     40  *  Returns a pointer to a D which comes byteOffset bytes after S.
     41  */
     42 template <typename D, typename S> static D* SkTAddOffset(S* ptr, size_t byteOffset) {
     43     // The intermediate char* has the same cv-ness as D as this produces better error messages.
     44     // This relies on the fact that reinterpret_cast can add constness, but cannot remove it.
     45     return reinterpret_cast<D*>(reinterpret_cast<sknonstd::same_cv_t<char, D>*>(ptr) + byteOffset);
     46 }
     47 
     48 template <typename R, typename T, R (*P)(T*)> struct SkFunctionWrapper {
     49     R operator()(T* t) { return P(t); }
     50 };
     51 
     52 /** \class SkAutoTCallVProc
     53 
     54     Call a function when this goes out of scope. The template uses two
     55     parameters, the object, and a function that is to be called in the destructor.
     56     If detach() is called, the object reference is set to null. If the object
     57     reference is null when the destructor is called, we do not call the
     58     function.
     59 */
     60 template <typename T, void (*P)(T*)> class SkAutoTCallVProc
     61     : public skstd::unique_ptr<T, SkFunctionWrapper<void, T, P>> {
     62 public:
     63     SkAutoTCallVProc(T* obj): skstd::unique_ptr<T, SkFunctionWrapper<void, T, P>>(obj) {}
     64 
     65     operator T*() const { return this->get(); }
     66     T* detach() { return this->release(); }
     67 };
     68 
     69 /** \class SkAutoTCallIProc
     70 
     71 Call a function when this goes out of scope. The template uses two
     72 parameters, the object, and a function that is to be called in the destructor.
     73 If detach() is called, the object reference is set to null. If the object
     74 reference is null when the destructor is called, we do not call the
     75 function.
     76 */
     77 template <typename T, int (*P)(T*)> class SkAutoTCallIProc
     78     : public skstd::unique_ptr<T, SkFunctionWrapper<int, T, P>> {
     79 public:
     80     SkAutoTCallIProc(T* obj): skstd::unique_ptr<T, SkFunctionWrapper<int, T, P>>(obj) {}
     81 
     82     operator T*() const { return this->get(); }
     83     T* detach() { return this->release(); }
     84 };
     85 
     86 /** \class SkAutoTDelete
     87   An SkAutoTDelete<T> is like a T*, except that the destructor of SkAutoTDelete<T>
     88   automatically deletes the pointer it holds (if any).  That is, SkAutoTDelete<T>
     89   owns the T object that it points to.  Like a T*, an SkAutoTDelete<T> may hold
     90   either NULL or a pointer to a T object.  Also like T*, SkAutoTDelete<T> is
     91   thread-compatible, and once you dereference it, you get the threadsafety
     92   guarantees of T.
     93 
     94   The size of a SkAutoTDelete is small: sizeof(SkAutoTDelete<T>) == sizeof(T*)
     95 */
     96 template <typename T> class SkAutoTDelete : public skstd::unique_ptr<T> {
     97 public:
     98     SkAutoTDelete(T* obj = NULL) : skstd::unique_ptr<T>(obj) {}
     99 
    100     operator T*() const { return this->get(); }
    101     void free() { this->reset(nullptr); }
    102     T* detach() { return this->release(); }
    103 };
    104 
    105 template <typename T> class SkAutoTDeleteArray : public skstd::unique_ptr<T[]> {
    106 public:
    107     SkAutoTDeleteArray(T array[]) : skstd::unique_ptr<T[]>(array) {}
    108 
    109     void free() { this->reset(nullptr); }
    110     T* detach() { return this->release(); }
    111 };
    112 
    113 /** Allocate an array of T elements, and free the array in the destructor
    114  */
    115 template <typename T> class SkAutoTArray : SkNoncopyable {
    116 public:
    117     SkAutoTArray() {
    118         fArray = NULL;
    119         SkDEBUGCODE(fCount = 0;)
    120     }
    121     /** Allocate count number of T elements
    122      */
    123     explicit SkAutoTArray(int count) {
    124         SkASSERT(count >= 0);
    125         fArray = NULL;
    126         if (count) {
    127             fArray = new T[count];
    128         }
    129         SkDEBUGCODE(fCount = count;)
    130     }
    131 
    132     /** Reallocates given a new count. Reallocation occurs even if new count equals old count.
    133      */
    134     void reset(int count) {
    135         delete[] fArray;
    136         SkASSERT(count >= 0);
    137         fArray = NULL;
    138         if (count) {
    139             fArray = new T[count];
    140         }
    141         SkDEBUGCODE(fCount = count;)
    142     }
    143 
    144     ~SkAutoTArray() { delete[] fArray; }
    145 
    146     /** Return the array of T elements. Will be NULL if count == 0
    147      */
    148     T* get() const { return fArray; }
    149 
    150     /** Return the nth element in the array
    151      */
    152     T&  operator[](int index) const {
    153         SkASSERT((unsigned)index < (unsigned)fCount);
    154         return fArray[index];
    155     }
    156 
    157     void swap(SkAutoTArray& other) {
    158         SkTSwap(fArray, other.fArray);
    159         SkDEBUGCODE(SkTSwap(fCount, other.fCount));
    160     }
    161 
    162 private:
    163     T*  fArray;
    164     SkDEBUGCODE(int fCount;)
    165 };
    166 
    167 /** Wraps SkAutoTArray, with room for kCountRequested elements preallocated.
    168  */
    169 template <int kCountRequested, typename T> class SkAutoSTArray : SkNoncopyable {
    170 public:
    171     /** Initialize with no objects */
    172     SkAutoSTArray() {
    173         fArray = NULL;
    174         fCount = 0;
    175     }
    176 
    177     /** Allocate count number of T elements
    178      */
    179     SkAutoSTArray(int count) {
    180         fArray = NULL;
    181         fCount = 0;
    182         this->reset(count);
    183     }
    184 
    185     ~SkAutoSTArray() {
    186         this->reset(0);
    187     }
    188 
    189     /** Destroys previous objects in the array and default constructs count number of objects */
    190     void reset(int count) {
    191         T* start = fArray;
    192         T* iter = start + fCount;
    193         while (iter > start) {
    194             (--iter)->~T();
    195         }
    196 
    197         if (fCount != count) {
    198             if (fCount > kCount) {
    199                 // 'fArray' was allocated last time so free it now
    200                 SkASSERT((T*) fStorage != fArray);
    201                 sk_free(fArray);
    202             }
    203 
    204             if (count > kCount) {
    205                 const uint64_t size64 = sk_64_mul(count, sizeof(T));
    206                 const size_t size = static_cast<size_t>(size64);
    207                 if (size != size64) {
    208                     sk_out_of_memory();
    209                 }
    210                 fArray = (T*) sk_malloc_throw(size);
    211             } else if (count > 0) {
    212                 fArray = (T*) fStorage;
    213             } else {
    214                 fArray = NULL;
    215             }
    216 
    217             fCount = count;
    218         }
    219 
    220         iter = fArray;
    221         T* stop = fArray + count;
    222         while (iter < stop) {
    223             new (iter++) T;
    224         }
    225     }
    226 
    227     /** Return the number of T elements in the array
    228      */
    229     int count() const { return fCount; }
    230 
    231     /** Return the array of T elements. Will be NULL if count == 0
    232      */
    233     T* get() const { return fArray; }
    234 
    235     /** Return the nth element in the array
    236      */
    237     T&  operator[](int index) const {
    238         SkASSERT(index < fCount);
    239         return fArray[index];
    240     }
    241 
    242 private:
    243 #if defined(GOOGLE3)
    244     // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions
    245     // have multiple large stack allocations.
    246     static const int kMaxBytes = 4 * 1024;
    247     static const int kCount = kCountRequested * sizeof(T) > kMaxBytes
    248         ? kMaxBytes / sizeof(T)
    249         : kCountRequested;
    250 #else
    251     static const int kCount = kCountRequested;
    252 #endif
    253 
    254     int     fCount;
    255     T*      fArray;
    256     // since we come right after fArray, fStorage should be properly aligned
    257     char    fStorage[kCount * sizeof(T)];
    258 };
    259 
    260 /** Manages an array of T elements, freeing the array in the destructor.
    261  *  Does NOT call any constructors/destructors on T (T must be POD).
    262  */
    263 template <typename T> class SkAutoTMalloc : SkNoncopyable {
    264 public:
    265     /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
    266     explicit SkAutoTMalloc(T* ptr = NULL) {
    267         fPtr = ptr;
    268     }
    269 
    270     /** Allocates space for 'count' Ts. */
    271     explicit SkAutoTMalloc(size_t count) {
    272         fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW);
    273     }
    274 
    275     ~SkAutoTMalloc() {
    276         sk_free(fPtr);
    277     }
    278 
    279     /** Resize the memory area pointed to by the current ptr preserving contents. */
    280     void realloc(size_t count) {
    281         fPtr = reinterpret_cast<T*>(sk_realloc_throw(fPtr, count * sizeof(T)));
    282     }
    283 
    284     /** Resize the memory area pointed to by the current ptr without preserving contents. */
    285     T* reset(size_t count) {
    286         sk_free(fPtr);
    287         fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW);
    288         return fPtr;
    289     }
    290 
    291     T* get() const { return fPtr; }
    292 
    293     operator T*() {
    294         return fPtr;
    295     }
    296 
    297     operator const T*() const {
    298         return fPtr;
    299     }
    300 
    301     T& operator[](int index) {
    302         return fPtr[index];
    303     }
    304 
    305     const T& operator[](int index) const {
    306         return fPtr[index];
    307     }
    308 
    309     /**
    310      *  Releases the block back to the heap
    311      */
    312     void free() {
    313         this->reset(0);
    314     }
    315 
    316     /**
    317      *  Transfer ownership of the ptr to the caller, setting the internal
    318      *  pointer to NULL. Note that this differs from get(), which also returns
    319      *  the pointer, but it does not transfer ownership.
    320      */
    321     T* detach() {
    322         T* ptr = fPtr;
    323         fPtr = NULL;
    324         return ptr;
    325     }
    326 
    327 private:
    328     T* fPtr;
    329 };
    330 
    331 template <size_t kCountRequested, typename T> class SkAutoSTMalloc : SkNoncopyable {
    332 public:
    333     SkAutoSTMalloc() : fPtr(fTStorage) {}
    334 
    335     SkAutoSTMalloc(size_t count) {
    336         if (count > kCount) {
    337             fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
    338         } else {
    339             fPtr = fTStorage;
    340         }
    341     }
    342 
    343     ~SkAutoSTMalloc() {
    344         if (fPtr != fTStorage) {
    345             sk_free(fPtr);
    346         }
    347     }
    348 
    349     // doesn't preserve contents
    350     T* reset(size_t count) {
    351         if (fPtr != fTStorage) {
    352             sk_free(fPtr);
    353         }
    354         if (count > kCount) {
    355             fPtr = (T*)sk_malloc_throw(count * sizeof(T));
    356         } else {
    357             fPtr = fTStorage;
    358         }
    359         return fPtr;
    360     }
    361 
    362     T* get() const { return fPtr; }
    363 
    364     operator T*() {
    365         return fPtr;
    366     }
    367 
    368     operator const T*() const {
    369         return fPtr;
    370     }
    371 
    372     T& operator[](int index) {
    373         return fPtr[index];
    374     }
    375 
    376     const T& operator[](int index) const {
    377         return fPtr[index];
    378     }
    379 
    380     // Reallocs the array, can be used to shrink the allocation.  Makes no attempt to be intelligent
    381     void realloc(size_t count) {
    382         if (count > kCount) {
    383             if (fPtr == fTStorage) {
    384                 fPtr = (T*)sk_malloc_throw(count * sizeof(T));
    385                 memcpy(fPtr, fTStorage, kCount * sizeof(T));
    386             } else {
    387                 fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
    388             }
    389         } else if (fPtr != fTStorage) {
    390             fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
    391         }
    392     }
    393 
    394 private:
    395     // Since we use uint32_t storage, we might be able to get more elements for free.
    396     static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
    397 #if defined(GOOGLE3)
    398     // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions
    399     // have multiple large stack allocations.
    400     static const size_t kMaxBytes = 4 * 1024;
    401     static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
    402         ? kMaxBytes / sizeof(T)
    403         : kCountWithPadding;
    404 #else
    405     static const size_t kCount = kCountWithPadding;
    406 #endif
    407 
    408     T*          fPtr;
    409     union {
    410         uint32_t    fStorage32[SkAlign4(kCount*sizeof(T)) >> 2];
    411         T           fTStorage[1];   // do NOT want to invoke T::T()
    412     };
    413 };
    414 
    415 //////////////////////////////////////////////////////////////////////////////////////////////////
    416 
    417 /**
    418  *  Pass the object and the storage that was offered during SkInPlaceNewCheck, and this will
    419  *  safely destroy (and free if it was dynamically allocated) the object.
    420  */
    421 template <typename T> void SkInPlaceDeleteCheck(T* obj, void* storage) {
    422     if (storage == obj) {
    423         obj->~T();
    424     } else {
    425         delete obj;
    426     }
    427 }
    428 
    429 /**
    430  *  Allocates T, using storage if it is large enough, and allocating on the heap (via new) if
    431  *  storage is not large enough.
    432  *
    433  *      obj = SkInPlaceNewCheck<Type>(storage, size);
    434  *      ...
    435  *      SkInPlaceDeleteCheck(obj, storage);
    436  */
    437 template <typename T> T* SkInPlaceNewCheck(void* storage, size_t size) {
    438     return (sizeof(T) <= size) ? new (storage) T : new T;
    439 }
    440 
    441 template <typename T, typename A1, typename A2, typename A3>
    442 T* SkInPlaceNewCheck(void* storage, size_t size, const A1& a1, const A2& a2, const A3& a3) {
    443     return (sizeof(T) <= size) ? new (storage) T(a1, a2, a3) : new T(a1, a2, a3);
    444 }
    445 
    446 /**
    447  * Reserves memory that is aligned on double and pointer boundaries.
    448  * Hopefully this is sufficient for all practical purposes.
    449  */
    450 template <size_t N> class SkAlignedSStorage : SkNoncopyable {
    451 public:
    452     size_t size() const { return N; }
    453     void* get() { return fData; }
    454     const void* get() const { return fData; }
    455 
    456 private:
    457     union {
    458         void*   fPtr;
    459         double  fDouble;
    460         char    fData[N];
    461     };
    462 };
    463 
    464 /**
    465  * Reserves memory that is aligned on double and pointer boundaries.
    466  * Hopefully this is sufficient for all practical purposes. Otherwise,
    467  * we have to do some arcane trickery to determine alignment of non-POD
    468  * types. Lifetime of the memory is the lifetime of the object.
    469  */
    470 template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable {
    471 public:
    472     /**
    473      * Returns void* because this object does not initialize the
    474      * memory. Use placement new for types that require a cons.
    475      */
    476     void* get() { return fStorage.get(); }
    477     const void* get() const { return fStorage.get(); }
    478 private:
    479     SkAlignedSStorage<sizeof(T)*N> fStorage;
    480 };
    481 
    482 #endif
    483